Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

sockaddr_ptoa.c

Go to the documentation of this file.
00001 /*
00002 ** Copyright (C) 2005 by Kevin L. Mitchell <klmitch@mit.edu>
00003 **
00004 ** This program is free software; you can redistribute it and/or modify
00005 ** it under the terms of the GNU General Public License as published by
00006 ** the Free Software Foundation; either version 2 of the License, or
00007 ** (at your option) any later version.
00008 **
00009 ** This program is distributed in the hope that it will be useful,
00010 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 ** GNU General Public License for more details.
00013 **
00014 ** You should have received a copy of the GNU General Public License
00015 ** along with this program; if not, write to the Free Software
00016 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 **
00018 ** @(#)$Id: sockaddr_ptoa.c,v 1.2 2005/12/18 00:15:11 klmitch Exp $
00019 */
00027 #include "event_int.h"
00028 
00029 #include <string.h>
00030 
00031 RCSTAG("@(#)$Id: sockaddr_ptoa.c,v 1.2 2005/12/18 00:15:11 klmitch Exp $");
00032 
00040 #define VALMASK                 0x0f
00041 
00050 #define DIGIT                   0x10
00051 
00060 #define XDIGIT                  0x20
00061 
00069 #define COLON                   0x30
00070 
00079 #define PERIOD                  0x40
00080 
00087 #define TYPEMASK                0xf0
00088 
00103 #define ctype(c)                (cmap[(int)(c)] & TYPEMASK)
00104 
00116 #define cval(c)                 (cmap[(int)(c)] & VALMASK)
00117 
00123 static unsigned char cmap[] = {
00124   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00125   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00126   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00127   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x10, 0x11, 0x12, 0x13,
00128   0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00129   0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00130   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00131   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x00,
00132   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00133   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00134   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00135   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00136   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00137   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00138   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00139   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00140   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00141   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00142   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00143   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
00144 };
00145 
00158 #define _bcd(num, digit)        (((num) >> ((digit) << 2)) & 0x0f)
00159 
00173 #define bcd(num)                (_bcd((num), 3) * 1000 +                      \
00174                                  _bcd((num), 2) *  100 +                      \
00175                                  _bcd((num), 1) *   10 +                      \
00176                                  _bcd((num), 0) *    1)
00177 
00191 #define bcd_isquad(num)         ((num) <= 0x0255 && _bcd((num), 1) <= 9 &&    \
00192                                  _bcd((num), 0) <= 9)
00193 
00212 #define hex_accum(accum, c)     ((accum) = ((accum) << 4) | cval((c)))
00213 
00232 #define dec_accum(accum, c)     ((accum) = ((accum) * 10) + cval((c)))
00233 
00240 struct partaddr {
00241   unsigned char*addr[2];        
00242   unsigned char addr2[SOCK_ADDRBUFV6_LEN];
00244   int           segs[2];        
00245   int           cnt;            
00246   ev_sockaddr_t*result;         
00247 };
00248 
00260 #define partaddr_init(pa, res)                                                \
00261 do {                                                                          \
00262   struct partaddr *_pa = (pa);                                                \
00263   ev_sockaddr_t *_res = (res);                                                \
00264   int _i;                                                                     \
00265   _pa->addr[0] = sa_ipaddrbuf(_res); /* set partial addr pointers... */       \
00266   _pa->addr[1] = _pa->addr2;                                                  \
00267   for (_i = 0; _i < SOCK_ADDRBUFV6_LEN; _i++) /* initialize addresses */      \
00268     _pa->addr[1][_i] = 0;                                                     \
00269   _pa->segs[0] = _pa->segs[1] = _pa->cnt = 0; /* initialize counters... */    \
00270   _pa->result = _res; /* and remember where the result is */                  \
00271 } while (0)
00272 
00283 #define accumulate(pa, accum)                                                 \
00284 do {                                                                          \
00285   struct partaddr *_pa = (pa);                                                \
00286   uint32_t _accum = (accum);                                                  \
00287   if (_pa->segs[0] + _pa->segs[1] + _pa->cnt + 1 >                            \
00288       (sa_type(_pa->result) == AT_IPv4 ?                                      \
00289        SOCK_ADDRBUFV4_LEN : SOCK_ADDRBUFV6_LEN))                              \
00290     ev_return(EV_ERR_ADDRTOOLONG); /* too many segments, bail out */          \
00291   _pa->addr[_pa->cnt][_pa->segs[_pa->cnt]++] = (_accum >> 8) & 0xff;          \
00292   _pa->addr[_pa->cnt][_pa->segs[_pa->cnt]++] = (_accum     ) & 0xff;          \
00293 } while (0)
00294 
00305 #define test_v6(pa)                                                           \
00306 do {                                                                          \
00307   struct partaddr *_pa = (pa);                                                \
00308   if (!_pa->cnt && _pa->segs[0] != SOCK_ADDRBUFV6_LEN)                        \
00309     ev_return(EV_ERR_ADDRINCOMPLETE); /* malformed address, bail out */       \
00310 } while (0)
00311 
00312 ev_err_t
00313 sockaddr_ptoa(ev_sockaddr_t *sa, const char *address, int alen)
00314 {
00315   const unsigned char *t = (const unsigned char *)address;
00316   unsigned char lastc = '\0';
00317   enum { s_unspec, s_inet4, s_inet6, s_inet64, s_port } state = s_unspec;
00318   uint32_t accum = 0, accum2 = 0; /* gotta be large enough for overflow */
00319   struct partaddr pa;
00320   int dots = 0, i, j, doublec = 0, twoc = 0;
00321   ev_satype_t subtype = AT_NONE;
00322 
00323   ev_init(); /* make sure library is initialized... */
00324 
00325   if (!sa || !address) /* sanity-check arguments */
00326     ev_return(EINVAL);
00327 
00328   sa->sa_type = AT_NONE; /* initialize the resultant ev_sockaddr_t */
00329   memset((void *)&sa->sa_addr, 0, sizeof(sa->sa_addr));
00330 
00331   if (alen < 0) /* Length not set? */
00332     alen = strlen(address);
00333 
00334   if (*address == '/') { /* Ah ha, we have an AT_LOCAL address... */
00335     sa->sa_type = AT_LOCAL; /* set the type */
00336     if (alen >= SOCK_LOCALADDR_LEN)
00337       ev_return(EV_ERR_ADDRTOOLONG); /* address is too long to fit! */
00338     strncpy(sa->sa_addr.saa_localaddr, address, alen); /* copy the address */
00339     ev_return(0); /* we're all done!  Gee, that was easy... */
00340   }
00341 
00342   /* ...but this isn't!  Run a state-based parser to disassemble the address */
00343 
00344   partaddr_init(&pa, sa); /* initialize the struct partaddr */
00345 
00346   /* OK, process the address string character by character */
00347   for (; alen && *t; lastc = *(t++), alen--)
00348     switch (ctype(*t)) {
00349     case XDIGIT: /* hexadecimal digit... */
00350       if (state == s_unspec) { /* hex digit implies IPv6 address */
00351         state = s_inet6;
00352         sa->sa_type = AT_IPv6;
00353         subtype = AT_IPv6;
00354       } else if (state != s_inet6)
00355         ev_return(EV_ERR_INVALIDCHAR); /* bad character in address... */
00356       /*FALLTHROUGH*/
00357     case DIGIT: /* decimal digit */
00358       if (doublec) /* expecting a ':'... */
00359         ev_return(EV_ERR_COLONEXPECTED);
00360       if (state == s_unspec || state == s_inet6)
00361         hex_accum(accum, *t); /* accumulate as a hex digit string */
00362       else
00363         dec_accum(accum, *t); /* accumulate as a decimal digit string */
00364 
00365       if (state == s_unspec && !bcd_isquad(accum)) {
00366         /* component is too big to be part of an IPv4 address */
00367         state = s_inet6; /* so it must be an IPv6 address! */
00368         sa->sa_type = AT_IPv6;
00369         subtype = AT_IPv6;
00370       }
00371 
00372       if (accum > (state == s_inet4 ? 255 : 65535)) /* check for overflow */
00373         ev_return(EV_ERR_ADDRTOOLONG);
00374 
00375       twoc = 0; /* zero twoc... */
00376       break;
00377 
00378     case COLON:
00379       if (state == s_unspec) { /* OK, ':' implies IPv6 address */
00380         state = s_inet6;
00381         sa->sa_type = AT_IPv6;
00382         subtype = AT_IPv6;
00383         if (!lastc) /* first character is a colon? */
00384           doublec++; /* must be followed by another! */
00385       } else if (state != s_inet6)
00386         ev_return(EV_ERR_INVALIDCHAR); /* colon in non-IPv6 address? */
00387       else
00388         doublec = 0; /* zero doublec... */
00389 
00390       if (lastc == ':') { /* two ':' in a row; segment of zeros... */
00391         if (pa.cnt++) /* was there a previous one? */
00392           ev_return(EV_ERR_ADDRTOOLONG); /* too many zero runs */
00393         twoc++; /* have two colons in a row... */
00394       }
00395 
00396       accumulate(&pa, accum); /* OK, add component to address */
00397       accum = 0; /* and reset the accumulator */
00398       break;
00399 
00400     case PERIOD:
00401       if ((!twoc && lastc == ':') || /* . after a :?  Only if 2 in a row... */
00402           lastc == '.' || /* . after a .?  That doesn't make sense! */
00403           (dots && *address == '.')) /* .a.b -- malformed IPv4 address */
00404         ev_return(EV_ERR_INVALIDCHAR);
00405       dots++; /* increment the count of periods */
00406       twoc = 0; /* zero twoc */
00407       if (state == s_unspec) { /* OK, this is an IPv4 address! */
00408         accum2 = bcd(accum); /* store first quad in second accumulator */
00409         accum = 0; /* prepare for the next quad */
00410         state = s_inet4; /* and switch states */
00411         sa->sa_type = AT_IPv4;
00412         subtype = AT_IPv4;
00413         continue; /* must process next character! */
00414       } else if (state == s_inet6) { /* processing an IPv6 address... */
00415         if (bcd_isquad(accum)) { /* it's valid as an IPv4 quad... */
00416 
00417           /* The accumulated value could be either an IPv4 quad or an
00418            * IPv6 address component.  We won't know until later: if we
00419            * get another period, then it was a quad followed by a
00420            * quad; if we hit the end of the string, then it was an
00421            * IPv6 address component followed by a port number.
00422            */
00423 
00424           accum2 = accum; /* save this component for later... */
00425           accum = 0; /* must zero the accumulator for next component */
00426           state = s_inet64; /* switch to the intermediate state */
00427         } else { /* definitely an IPv6 address component */
00428           accumulate(&pa, accum); /* accumulate it... */
00429           accum = 0; /* reset the accumulator */
00430           test_v6(&pa); /* verify the address is valid... */
00431           state = s_port; /* what follows is definitely a port number */
00432         }
00433         continue; /* must process next character */
00434       } else if (state == s_inet64) { /* a second '.'; must be a v4 address */
00435         accum2 = bcd(accum2); /* decode second accumulator */
00436         state = s_inet4; /* switch to the IPv4 processing state */
00437         subtype = AT_IPv4;
00438         /* Drop through to the processing stage */
00439       } else if (state == s_inet4) {
00440         if (dots == 4) { /* what follows this '.' must be a port number */
00441           if (sa->sa_type == AT_IPv6)
00442             test_v6(&pa); /* verify it's a valid IPv6 address */
00443           state = s_port; /* switch to port processing state */
00444         }
00445       } else /* a '.' after a port?  huh? */
00446         ev_return(EV_ERR_INVALIDCHAR); /* malformed address! */
00447 
00448       if (dots & 0x01) { /* OK, if it's odd, we have only one quad so far... */
00449         accum2 = accum; /* so save it and zero the accumulator */
00450         accum = 0;
00451       } else { /* two quads, add them to the address */
00452         accumulate(&pa, (accum2 << 8) | accum);
00453         accum = 0; /* zero the accumulators */
00454         accum2 = 0;
00455       }
00456       break;
00457 
00458     default: /* unknown character */
00459       ev_return(EV_ERR_INVALIDCHAR); /* malformed address! */
00460       break;
00461     }
00462 
00463   /* OK, we've hit the end of the string.  Normalize the state... */
00464   if (state == s_unspec || (state == s_inet4 && dots == 1 &&
00465                             *address == '.')) {
00466     if (lastc && !dots) /* there must be at least one '.' */
00467       ev_return(EV_ERR_BADADDR);
00468 
00469     /* OK, let's assume an IPv4 wildcard address... */
00470     sa->sa_type = AT_IPv4;
00471     if (state == s_inet4) /* accumulator will contain a port number */
00472       state = s_port;
00473   } else if (state == s_inet64) {
00474     /* previous segment was an IPv6 address component */
00475     accumulate(&pa, accum2);
00476     test_v6(&pa); /* verify it's a valid address */
00477     state = s_port; /* accumulator will contain a port number */
00478   }
00479 
00480   /* At this point, we have the address type, and we have reduced the
00481    * set of possible states to { s_inet4, s_inet6, s_port }.  If we're
00482    * in one of the inet states, the accumulator contains an address
00483    * component and the port number will be 0.  Otherwise, the
00484    * accumulator contains the port number, and the address is
00485    * complete.
00486    */
00487   if (state == s_inet4) { /* IPv4 address component... */
00488     if (dots < 3) /* at least 3 dots are necessary */
00489       ev_return(EV_ERR_ADDRINCOMPLETE);
00490     accumulate(&pa, (accum2 << 8) | accum); /* add the component */
00491     if (sa->sa_type == AT_IPv6)
00492       test_v6(&pa); /* verify that v6 address is complete */
00493   } else if (state == s_inet6) { /* IPv6 address component */
00494     if (!twoc && lastc == ':') /* cannot end with ':' except in '::' case */
00495       ev_return(EV_ERR_INVALIDCHAR); /* malformed address... */
00496     accumulate(&pa, accum); /* add the address component */
00497     test_v6(&pa); /* verify that the address is complete */
00498   } else /* accumulator contains a port number */
00499     sa->sa_addr.saa_ipaddr.saai_port = accum; /* so remember it! */
00500 
00501   /* OK, one final step: If the v6 address contained '::', we need to
00502    * merge the two pieces of the address into one.  We do this by
00503    * walking backwards through the two pieces, starting at the very
00504    * end of piece 0 (the result) and at the last segment of piece 1.
00505    */
00506   for (i = SOCK_ADDRBUFV6_LEN - 1, j = pa.segs[1] - 1; i >= 0 && j >= 0;
00507        i--, j--)
00508     pa.addr[0][i] = pa.addr[1][j]; /* just do an assignment */
00509 
00510   /* Because of our pointer tricks with the struct partaddr, the final
00511    * answer is now contained in the ev_sockaddr_t we were passed to
00512    * begin with, and so we are now done.
00513    */
00514   ev_return(0);
00515 }

Generated on Wed Dec 28 23:36:56 2005 for event by  doxygen 1.4.4