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