1/* 2 * Part of Very Secure FTPd 3 * Licence: GPL v2 4 * Author: Chris Evans 5 * ipaddrparse.c 6 * 7 * A routine to parse ip addresses. I'm paranoid and don't want to use 8 * inet_pton. 9 */ 10 11#include "ipaddrparse.h" 12#include "sysutil.h" 13#include "str.h" 14 15static int ipv6_parse_main(struct mystr* p_out_str, 16 const struct mystr* p_in_str); 17static int ipv6_parse_hex(struct mystr* p_out_str, 18 const struct mystr* p_in_str); 19static int ipv4_parse_dotquad(struct mystr* p_out_str, 20 const struct mystr* p_in_str); 21 22const unsigned char* 23vsf_sysutil_parse_ipv6(const struct mystr* p_str) 24{ 25 static struct mystr s_ret; 26 static struct mystr s_rhs_ret; 27 static struct mystr s_lhs_str; 28 static struct mystr s_rhs_str; 29 unsigned int lhs_len; 30 unsigned int rhs_len; 31 str_empty(&s_ret); 32 str_empty(&s_rhs_ret); 33 str_copy(&s_lhs_str, p_str); 34 str_split_text(&s_lhs_str, &s_rhs_str, "::"); 35 if (!ipv6_parse_main(&s_ret, &s_lhs_str)) 36 { 37 return 0; 38 } 39 if (!ipv6_parse_main(&s_rhs_ret, &s_rhs_str)) 40 { 41 return 0; 42 } 43 lhs_len = str_getlen(&s_ret); 44 rhs_len = str_getlen(&s_rhs_ret); 45 if (lhs_len + rhs_len > 16) 46 { 47 return 0; 48 } 49 if (rhs_len > 0) 50 { 51 unsigned int add_nulls = 16 - (lhs_len + rhs_len); 52 while (add_nulls--) 53 { 54 str_append_char(&s_ret, '\0'); 55 } 56 str_append_str(&s_ret, &s_rhs_ret); 57 } 58 return (unsigned char*)str_getbuf(&s_ret); 59} 60 61const unsigned char* 62vsf_sysutil_parse_ipv4(const struct mystr* p_str) 63{ 64 static unsigned char items[4]; 65 return vsf_sysutil_parse_uchar_string_sep(p_str, '.', items, sizeof(items)); 66} 67 68const unsigned char* 69vsf_sysutil_parse_uchar_string_sep( 70 const struct mystr* p_str, char sep, unsigned char* p_items, 71 unsigned int items) 72{ 73 static struct mystr s_tmp_str; 74 unsigned int i; 75 str_copy(&s_tmp_str, p_str); 76 for (i=0; i<items; i++) 77 { 78 static struct mystr s_rhs_sep_str; 79 int this_number; 80 /* This puts a single separator delimited field in tmp_str */ 81 str_split_char(&s_tmp_str, &s_rhs_sep_str, sep); 82 /* Sanity - check for too many or two few dots! */ 83 if ( (i < (items-1) && str_isempty(&s_rhs_sep_str)) || 84 (i == (items-1) && !str_isempty(&s_rhs_sep_str))) 85 { 86 return 0; 87 } 88 this_number = str_atoi(&s_tmp_str); 89 if (this_number < 0 || this_number > 255) 90 { 91 return 0; 92 } 93 /* If this truncates from int to uchar, we don't care */ 94 p_items[i] = (unsigned char) this_number; 95 /* The right hand side of the comma now becomes the new string to 96 * breakdown 97 */ 98 str_copy(&s_tmp_str, &s_rhs_sep_str); 99 } 100 return p_items; 101} 102 103static int 104ipv6_parse_main(struct mystr* p_out_str, const struct mystr* p_in_str) 105{ 106 static struct mystr s_lhs_str; 107 static struct mystr s_rhs_str; 108 struct str_locate_result loc_ret; 109 str_copy(&s_lhs_str, p_in_str); 110 while (!str_isempty(&s_lhs_str)) 111 { 112 str_split_char(&s_lhs_str, &s_rhs_str, ':'); 113 if (str_isempty(&s_lhs_str)) 114 { 115 return 0; 116 } 117 loc_ret = str_locate_char(&s_lhs_str, '.'); 118 if (loc_ret.found) 119 { 120 if (!ipv4_parse_dotquad(p_out_str, &s_lhs_str)) 121 { 122 return 0; 123 } 124 } 125 else if (!ipv6_parse_hex(p_out_str, &s_lhs_str)) 126 { 127 return 0; 128 } 129 str_copy(&s_lhs_str, &s_rhs_str); 130 } 131 return 1; 132} 133 134static int 135ipv6_parse_hex(struct mystr* p_out_str, const struct mystr* p_in_str) 136{ 137 unsigned int len = str_getlen(p_in_str); 138 unsigned int i; 139 unsigned int val = 0; 140 for (i=0; i<len; ++i) 141 { 142 int ch = vsf_sysutil_toupper(str_get_char_at(p_in_str, i)); 143 if (ch >= '0' && ch <= '9') 144 { 145 ch -= '0'; 146 } 147 else if (ch >= 'A' && ch <= 'F') 148 { 149 ch -= 'A'; 150 ch += 10; 151 } 152 else 153 { 154 return 0; 155 } 156 val <<= 4; 157 val |= ch; 158 if (val > 0xFFFF) 159 { 160 return 0; 161 } 162 } 163 str_append_char(p_out_str, (val >> 8)); 164 str_append_char(p_out_str, (val & 0xFF)); 165 return 1; 166} 167 168static int 169ipv4_parse_dotquad(struct mystr* p_out_str, const struct mystr* p_in_str) 170{ 171 unsigned int len = str_getlen(p_in_str); 172 unsigned int i; 173 unsigned int val = 0; 174 unsigned int final_val = 0; 175 int seen_char = 0; 176 int dots = 0; 177 for (i=0; i<len; ++i) 178 { 179 int ch = str_get_char_at(p_in_str, i); 180 if (ch == '.') 181 { 182 if (!seen_char || dots == 3) 183 { 184 return 0; 185 } 186 seen_char = 0; 187 dots++; 188 final_val <<= 8; 189 final_val |= val; 190 val = 0; 191 } 192 else if (ch >= '0' && ch <= '9') 193 { 194 ch -= '0'; 195 val *= 10; 196 val += ch; 197 if (val > 255) 198 { 199 return 0; 200 } 201 seen_char = 1; 202 } 203 else 204 { 205 return 0; 206 } 207 } 208 if (dots != 3 || !seen_char) 209 { 210 return 0; 211 } 212 final_val <<= 8; 213 final_val |= val; 214 str_append_char(p_out_str, (final_val >> 24)); 215 str_append_char(p_out_str, ((final_val >> 16) & 0xFF)); 216 str_append_char(p_out_str, ((final_val >> 8) & 0xFF)); 217 str_append_char(p_out_str, (final_val & 0xFF)); 218 return 1; 219} 220 221