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