1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-05-18     ChenYong     First version
9  */
10 #include <stdlib.h>
11 #include <rtthread.h>
12 #include <netdev_ipaddr.h>
13 
14 /* Here for now until needed in other places in lwIP */
15 #ifndef isprint
16 #define in_range(c, lo, up)  ((uint8_t)c >= lo && (uint8_t)c <= up)
17 #define isprint(c)           in_range(c, 0x20, 0x7f)
18 #define isdigit(c)           in_range(c, '0', '9')
19 #define isxdigit(c)          (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
20 #define islower(c)           in_range(c, 'a', 'z')
21 #define isspace(c)           (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
22 #define xchar(i)             ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
23 #endif
24 
25 #if NETDEV_IPV4
26 
27 /**
28  * Check whether "cp" is a valid ascii representation
29  * of an Internet address and convert to a binary address.
30  * Returns 1 if the address is valid, 0 if not.
31  * This replaces inet_addr, the return value from which
32  * cannot distinguish between failure and a local broadcast address.
33  *
34  * @param cp IP address in ascii representation (e.g. "127.0.0.1")
35  * @param addr pointer to which to save the ip address in network order
36  * @return 1 if cp could be converted to addr, 0 on failure
37  */
netdev_ip4addr_aton(const char * cp,ip4_addr_t * addr)38 int netdev_ip4addr_aton(const char *cp, ip4_addr_t *addr)
39 {
40     uint32_t val;
41     uint8_t base;
42     char c;
43     uint32_t parts[4];
44     uint32_t *pp = parts;
45 
46     c = *cp;
47     for (;;)
48     {
49         /*
50          * Collect number up to ``.''.
51          * Values are specified as for C:
52          * 0x=hex, 0=octal, 1-9=decimal.
53          */
54         if (!isdigit(c))
55         {
56             return 0;
57         }
58         val = 0;
59         base = 10;
60         if (c == '0')
61         {
62             c = *++cp;
63             if (c == 'x' || c == 'X')
64             {
65                 base = 16;
66                 c = *++cp;
67             }
68             else
69             {
70                 base = 8;
71             }
72         }
73         for (;;)
74         {
75             if (isdigit(c))
76             {
77                 val = (val * base) + (uint32_t) (c - '0');
78                 c = *++cp;
79             }
80             else if (base == 16 && isxdigit(c))
81             {
82                 val = (val << 4) | (uint32_t) (c + 10 - (islower(c) ? 'a' : 'A'));
83                 c = *++cp;
84             }
85             else
86             {
87                 break;
88             }
89         }
90         if (c == '.')
91         {
92             /*
93              * Internet format:
94              *  a.b.c.d
95              *  a.b.c   (with c treated as 16 bits)
96              *  a.b (with b treated as 24 bits)
97              */
98             if (pp >= parts + 3)
99             {
100                 return 0;
101             }
102             *pp++ = val;
103             c = *++cp;
104         }
105         else
106         {
107             break;
108         }
109     }
110     /*
111      * Check for trailing characters.
112      */
113     if (c != '\0' && !isspace(c))
114     {
115         return 0;
116     }
117     /*
118      * Concoct the address according to
119      * the number of parts specified.
120      */
121     switch (pp - parts + 1)
122     {
123 
124     case 0:
125         return 0; /* initial nondigit */
126 
127     case 1: /* a -- 32 bits */
128         break;
129 
130     case 2: /* a.b -- 8.24 bits */
131         if (val > 0xffffffUL)
132         {
133             return 0;
134         }
135         if (parts[0] > 0xff)
136         {
137             return 0;
138         }
139         val |= parts[0] << 24;
140         break;
141 
142     case 3: /* a.b.c -- 8.8.16 bits */
143         if (val > 0xffff)
144         {
145             return 0;
146         }
147         if ((parts[0] > 0xff) || (parts[1] > 0xff))
148         {
149             return 0;
150         }
151         val |= (parts[0] << 24) | (parts[1] << 16);
152         break;
153 
154     case 4: /* a.b.c.d -- 8.8.8.8 bits */
155         if (val > 0xff)
156         {
157             return 0;
158         }
159         if ((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff))
160         {
161             return 0;
162         }
163         val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
164         break;
165     default:
166         RT_ASSERT(0);
167         break;
168     }
169     if (addr)
170     {
171         ip4_addr_set_u32(addr, htonl(val));
172     }
173     return 1;
174 }
175 
176 /**
177  * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
178  *
179  * @param addr ip address in network order to convert
180  * @param buf target buffer where the string is stored
181  * @param buflen length of buf
182  * @return either pointer to buf which now holds the ASCII
183  *         representation of addr or NULL if buf was too small
184  */
netdev_ip4addr_ntoa_r(const ip4_addr_t * addr,char * buf,int buflen)185 char *netdev_ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen)
186 {
187     uint32_t s_addr;
188     char inv[3];
189     char *rp;
190     uint8_t *ap;
191     uint8_t rem;
192     uint8_t n;
193     uint8_t i;
194     int len = 0;
195 
196     s_addr = ip4_addr_get_u32(addr);
197 
198     rp = buf;
199     ap = (uint8_t *) &s_addr;
200     for (n = 0; n < 4; n++)
201     {
202         i = 0;
203         do
204         {
205             rem = *ap % (uint8_t) 10;
206             *ap /= (uint8_t) 10;
207             inv[i++] = (char) ('0' + rem);
208         } while (*ap);
209         while (i--)
210         {
211             if (len++ >= buflen)
212             {
213                 return NULL;
214             }
215             *rp++ = inv[i];
216         }
217         if (len++ >= buflen)
218         {
219             return NULL;
220         }
221         *rp++ = '.';
222         ap++;
223     }
224     *--rp = 0;
225     return buf;
226 }
227 
228 
229 /**
230  * Convert numeric IP address into decimal dotted ASCII representation.
231  * returns ptr to static buffer; not reentrant!
232  *
233  * @param addr ip address in network order to convert
234  * @return pointer to a global static (!) buffer that holds the ASCII
235  *         representation of addr
236  */
netdev_ip4addr_ntoa(const ip4_addr_t * addr)237 char *netdev_ip4addr_ntoa(const ip4_addr_t *addr)
238 {
239     static char str[IP4ADDR_STRLEN_MAX];
240     return netdev_ip4addr_ntoa_r(addr, str, IP4ADDR_STRLEN_MAX);
241 }
242 
243 
244 /**
245  * Ascii internet address interpretation routine.
246  * The value returned is in network order.
247  *
248  * @param cp IP address in ascii representation (e.g. "127.0.0.1")
249  * @return ip address in network order
250  */
netdev_ipaddr_addr(const char * cp)251 in_addr_t netdev_ipaddr_addr(const char *cp)
252 {
253     ip4_addr_t val;
254 
255     if (netdev_ip4addr_aton(cp, &val)) {
256         return ip4_addr_get_u32(&val);
257     }
258     return (IPADDR_NONE);
259 }
260 
261 #endif /* NETDEV_IPV4 */
262 
263 
264 #if NETDEV_IPV6
265 
266 rt_weak const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
267 
268 /**
269  * Check whether "cp" is a valid ascii representation
270  * of an IPv6 address and convert to a binary address.
271  * Returns 1 if the address is valid, 0 if not.
272  *
273  * @param cp IPv6 address in ascii representation (e.g. "FF01::1")
274  * @param addr pointer to which to save the ip address in network order
275  * @return 1 if cp could be converted to addr, 0 on failure
276  */
277 int
netdev_ip6addr_aton(const char * cp,ip6_addr_t * addr)278 netdev_ip6addr_aton(const char *cp, ip6_addr_t *addr)
279 {
280     uint32_t addr_index, zero_blocks, current_block_index, current_block_value;
281     const char *s;
282 
283     /* Count the number of colons, to count the number of blocks in a "::" sequence
284      zero_blocks may be 1 even if there are no :: sequences */
285     zero_blocks = 8;
286     for (s = cp; *s != 0; s++)
287     {
288         if (*s == ':')
289         {
290             zero_blocks--;
291         }
292         else if (!isxdigit(*s))
293         {
294             break;
295         }
296     }
297 
298     /* parse each block */
299     addr_index = 0;
300     current_block_index = 0;
301     current_block_value = 0;
302     for (s = cp; *s != 0; s++)
303     {
304         if (*s == ':')
305         {
306             if (addr)
307             {
308                 if (current_block_index & 0x1)
309                 {
310                     addr->addr[addr_index++] |= current_block_value;
311                 }
312                 else
313                 {
314                     addr->addr[addr_index] = current_block_value << 16;
315                 }
316             }
317             current_block_index++;
318             current_block_value = 0;
319             if (current_block_index > 7)
320             {
321                 /* address too long! */
322                 return 0;
323             }
324             if (s[1] == ':')
325             {
326                 if (s[2] == ':')
327                 {
328                     /* invalid format: three successive colons */
329                     return 0;
330                 }
331                 s++;
332                 /* "::" found, set zeros */
333                 while (zero_blocks > 0)
334                 {
335                     zero_blocks--;
336                     if (current_block_index & 0x1)
337                     {
338                         addr_index++;
339                     }
340                     else
341                     {
342                         if (addr)
343                         {
344                             addr->addr[addr_index] = 0;
345                         }
346                     }
347                     current_block_index++;
348                     if (current_block_index > 7)
349                     {
350                         /* address too long! */
351                         return 0;
352                     }
353                 }
354             }
355         }
356         else if (isxdigit(*s))
357         {
358             /* add current digit */
359             current_block_value = (current_block_value << 4) +
360                                   (isdigit(*s) ? (uint32_t)(*s - '0') : (uint32_t)(10 + (islower(*s) ? *s - 'a' : *s - 'A')));
361         }
362         else
363         {
364             /* unexpected digit, space? CRLF? */
365             break;
366         }
367     }
368 
369     if (addr)
370     {
371         if (current_block_index & 0x1)
372         {
373             addr->addr[addr_index++] |= current_block_value;
374         }
375         else
376         {
377             addr->addr[addr_index] = current_block_value << 16;
378         }
379     }
380 
381     /* convert to network byte order. */
382     if (addr)
383     {
384         for (addr_index = 0; addr_index < 4; addr_index++)
385         {
386             addr->addr[addr_index] = htonl(addr->addr[addr_index]);
387         }
388     }
389 
390     if (current_block_index != 7)
391     {
392         return 0;
393     }
394 
395     return 1;
396 }
397 
398 /**
399  * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
400  *
401  * @param addr ip6 address in network order to convert
402  * @param buf target buffer where the string is stored
403  * @param buflen length of buf
404  * @return either pointer to buf which now holds the ASCII
405  *         representation of addr or NULL if buf was too small
406  */
407 char *
netdev_ip6addr_ntoa_r(const ip6_addr_t * addr,char * buf,int buflen)408 netdev_ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
409 {
410     uint32_t current_block_index, current_block_value, next_block_value;
411     int32_t i;
412     uint8_t zero_flag, empty_block_flag;
413 
414     i = 0;
415     empty_block_flag = 0; /* used to indicate a zero chain for "::' */
416 
417     for (current_block_index = 0; current_block_index < 8; current_block_index++)
418     {
419         /* get the current 16-bit block */
420         current_block_value = htonl(addr->addr[current_block_index >> 1]);
421         if ((current_block_index & 0x1) == 0)
422         {
423             current_block_value = current_block_value >> 16;
424         }
425         current_block_value &= 0xffff;
426 
427         /* Check for empty block. */
428         if (current_block_value == 0)
429         {
430             if (current_block_index == 7 && empty_block_flag == 1)
431             {
432                 /* special case, we must render a ':' for the last block. */
433                 buf[i++] = ':';
434                 if (i >= buflen)
435                 {
436                     return NULL;
437                 }
438                 break;
439             }
440             if (empty_block_flag == 0)
441             {
442                 /* generate empty block "::", but only if more than one contiguous zero block,
443          * according to current formatting suggestions RFC 5952. */
444                 next_block_value = htonl(addr->addr[(current_block_index + 1) >> 1]);
445                 if ((current_block_index & 0x1) == 0x01)
446                 {
447                     next_block_value = next_block_value >> 16;
448                 }
449                 next_block_value &= 0xffff;
450                 if (next_block_value == 0)
451                 {
452                     empty_block_flag = 1;
453                     buf[i++] = ':';
454                     if (i >= buflen)
455                     {
456                         return NULL;
457                     }
458                     continue; /* move on to next block. */
459                 }
460             }
461             else if (empty_block_flag == 1)
462             {
463                 /* move on to next block. */
464                 continue;
465             }
466         }
467         else if (empty_block_flag == 1)
468         {
469             /* Set this flag value so we don't produce multiple empty blocks. */
470             empty_block_flag = 2;
471         }
472 
473         if (current_block_index > 0)
474         {
475             buf[i++] = ':';
476             if (i >= buflen)
477             {
478                 return NULL;
479             }
480         }
481 
482         if ((current_block_value & 0xf000) == 0)
483         {
484             zero_flag = 1;
485         }
486         else
487         {
488             buf[i++] = xchar(((current_block_value & 0xf000) >> 12));
489             zero_flag = 0;
490             if (i >= buflen)
491             {
492                 return NULL;
493             }
494         }
495 
496         if (((current_block_value & 0xf00) == 0) && (zero_flag))
497         {
498             /* do nothing */
499         }
500         else
501         {
502             buf[i++] = xchar(((current_block_value & 0xf00) >> 8));
503             zero_flag = 0;
504             if (i >= buflen)
505             {
506                 return NULL;
507             }
508         }
509 
510         if (((current_block_value & 0xf0) == 0) && (zero_flag))
511         {
512             /* do nothing */
513         }
514         else
515         {
516             buf[i++] = xchar(((current_block_value & 0xf0) >> 4));
517             zero_flag = 0;
518             if (i >= buflen)
519             {
520                 return NULL;
521             }
522         }
523 
524         buf[i++] = xchar((current_block_value & 0xf));
525         if (i >= buflen)
526         {
527             return NULL;
528         }
529     }
530 
531     buf[i] = 0;
532 
533     return buf;
534 }
535 
536 /**
537  * Convert numeric IPv6 address into ASCII representation.
538  * returns ptr to static buffer; not reentrant!
539  *
540  * @param addr ip6 address in network order to convert
541  * @return pointer to a global static (!) buffer that holds the ASCII
542  *         representation of addr
543  */
544 char *
netdev_ip6addr_ntoa(const ip6_addr_t * addr)545 netdev_ip6addr_ntoa(const ip6_addr_t *addr)
546 {
547   static char str[40];
548   return netdev_ip6addr_ntoa_r(addr, str, 40);
549 }
550 
551 #endif /* NETDEV_IPV6 */
552 
553 const char *
netdev_inet_ntop(int af,const void * src,char * dst,int32_t size)554 netdev_inet_ntop(int af, const void *src, char *dst, int32_t size)
555 {
556 #define AF_INET         2
557 #define AF_INET6        10
558 
559     const char *ret = NULL;
560     int size_int = (int)size;
561     if (size_int < 0)
562     {
563         return NULL;
564     }
565     switch (af)
566     {
567 #if NETDEV_IPV4
568     case AF_INET:
569         return netdev_ip4addr_ntoa_r((const ip4_addr_t *)src, dst, size_int);
570 #endif
571 #if NETDEV_IPV6
572     case AF_INET6:
573         return netdev_ip6addr_ntoa_r((const ip6_addr_t *)src, dst, size_int);
574 #endif
575     default:
576         break;
577     }
578     return ret;
579 }
580 
581 int
netdev_inet_pton(int af,const char * src,void * dst)582 netdev_inet_pton(int af, const char *src, void *dst)
583 {
584 #define AF_INET         2
585 #define AF_INET6        10
586 
587     int err;
588     switch (af)
589     {
590 #if NETDEV_IPV4
591     case AF_INET:
592         err = netdev_ip4addr_aton(src, (ip4_addr_t *)dst);
593         break;
594 #endif
595 #if NETDEV_IPV6
596     case AF_INET6:
597     {
598         /* convert into temporary variable since ip6_addr_t might be larger
599          than in6_addr when scopes are enabled */
600         ip6_addr_t addr;
601         err = netdev_ip6addr_aton(src, &addr);
602         if (err)
603         {
604             rt_memcpy(dst, &addr.addr, sizeof(addr.addr));
605         }
606         break;
607     }
608 #endif
609     default:
610         err = -1;
611         break;
612     }
613     return err;
614 }
615