nfs-ganesha 1.4

cidr_to_str.c

Go to the documentation of this file.
00001 /*
00002  * cidr_to_str() - Generate a textual representation of the given CIDR
00003  * subnet.
00004  */
00005 #ifdef HAVE_CONFIG_H
00006 #include "config.h"
00007 #endif
00008 
00009 #include <errno.h>
00010 #include <stdio.h>
00011 #include <stdlib.h>
00012 #include <string.h>
00013 
00014 #include "cidr.h"
00015 #include "abstract_mem.h"
00016 
00017 char *
00018 cidr_to_str(const CIDR *block, int flags)
00019 {
00020         int i;
00021         int zst, zcur, zlen, zmax;
00022         short pflen;
00023         short lzer; /* Last zero */
00024         char *toret;
00025         char tmpbuf[128]; /* We shouldn't need more than ~5 anywhere */
00026         CIDR *nmtmp;
00027         char *nmstr;
00028         int nmflags;
00029         uint8_t moct;
00030         uint16_t v6sect;
00031 
00032         /* Just in case */
00033         if( (block==NULL) || (block->proto==CIDR_NOPROTO) )
00034         {
00035                 errno = EINVAL;
00036                 return(NULL);
00037         }
00038         
00039         /*
00040          * Sanity: If we have both ONLYADDR and ONLYPFLEN, we really don't
00041          * have anything to *DO*...
00042          */
00043         if((flags & CIDR_ONLYADDR) && (flags & CIDR_ONLYPFLEN))
00044         {
00045                 errno = EINVAL;
00046                 return(NULL);
00047         }
00048         
00049         /*
00050          * Now, in any case, there's a maximum length for any address, which
00051          * is the completely expanded form of a v6-{mapped,compat} address
00052          * with a netmask instead of a prefix.  That's 8 pieces of 4
00053          * characters each (32), separated by :'s (+7=39), plus the slash
00054          * (+1=40), plus another separated-8*4 (+39=79), plus the trailing
00055          * null (+1=80).  We'll just allocate 128 for kicks.
00056          *
00057          * I'm not, at this time anyway, going to try and allocate only and
00058          * exactly as much as we need for any given address.  Whether
00059          * consumers of the library can count on this behavior...  well, I
00060          * haven't decided yet.  Lemme alone.
00061          */
00062         toret = gsh_malloc(128);
00063         if(toret==NULL)
00064         {
00065                 errno = ENOMEM;
00066                 return(NULL);
00067         }
00068         memset(toret, 0, 128);
00069 
00070         /*
00071          * If it's a v4 address, we mask off everything but the last 4
00072          * octets, and just proceed from there.
00073          */
00074         if( (block->proto==CIDR_IPV4 && !(flags & CIDR_FORCEV6))
00075            || (flags & CIDR_FORCEV4) )
00076         {
00077                 /* First off, creating the in-addr.arpa form is special */
00078                 if(flags & CIDR_REVERSE)
00079                 {
00080                         /*
00081                          * Build the d.c.b.a.in-addr.arpa form.  Note that we ignore
00082                          * flags like CIDR_VERBOSE and the like here, since they lead
00083                          * to non-valid reverse paths (or at least, paths that no DNS
00084                          * implementation will look for).  So it pretty much always
00085                          * looks exactly the same.  Also, we don't mess with dealing
00086                          * with netmaks or anything here; we just assume it's a
00087                          * host address, and treat it as such.
00088                          */
00089 
00090                         sprintf(toret, "%d.%d.%d.%d.in-addr.arpa",
00091                                         block->addr[15], block->addr[14],
00092                                         block->addr[13], block->addr[12]);
00093                         return(toret);
00094                 }
00095 
00096                 /* Are we bothering to show the address? */
00097                 if(!(flags & CIDR_ONLYPFLEN))
00098                 {
00099                         /* If we're USEV6'ing, add whatever prefixes we need */
00100                         if(flags & CIDR_USEV6)
00101                         {
00102                                 if(flags & CIDR_NOCOMPACT)
00103                                 {
00104                                         if(flags & CIDR_VERBOSE)
00105                                                 strcat(toret, "0000:0000:0000:0000:0000:");
00106                                         else
00107                                                 strcat(toret, "0:0:0:0:0:");
00108                                 }
00109                                 else
00110                                         strcat(toret, "::");
00111 
00112                                 if(flags & CIDR_USEV4COMPAT)
00113                                 {
00114                                         if(flags & CIDR_NOCOMPACT)
00115                                         {
00116                                                 if(flags & CIDR_VERBOSE)
00117                                                         strcat(toret, "0000:");
00118                                                 else
00119                                                         strcat(toret, "0:");
00120                                         }
00121                                 }
00122                                 else
00123                                         strcat(toret, "ffff:");
00124                         } /* USEV6 */
00125 
00126                         /* Now, slap on the v4 address */
00127                         for(i=12 ; i<=15 ; i++)
00128                         {
00129                                 sprintf(tmpbuf, "%u", (block->addr)[i]);
00130                                 strcat(toret, tmpbuf);
00131                                 if(i<15)
00132                                         strcat(toret, ".");
00133                         }
00134                 } /* ! ONLYPFLEN */
00135 
00136                 /* Are we bothering to show the pf/mask? */
00137                 if(!(flags & CIDR_ONLYADDR))
00138                 {
00139                         /*
00140                          * And the prefix/netmask.  Don't show the '/' if we're only
00141                          * showing the pflen/mask.
00142                          */
00143                         if(!(flags & CIDR_ONLYPFLEN))
00144                                 strcat(toret, "/");
00145 
00146                         /* Which are we showing? */
00147                         if(flags & CIDR_NETMASK)
00148                         {
00149                                 /*
00150                                  * In this case, we can just print out like the address
00151                                  * above.
00152                                  */
00153                                 for(i=12 ; i<=15 ; i++)
00154                                 {
00155                                         moct = (block->mask)[i];
00156                                         if(flags & CIDR_WILDCARD)
00157                                                 moct = ~(moct);
00158                                         sprintf(tmpbuf, "%u", moct);
00159                                         strcat(toret, tmpbuf);
00160                                         if(i<15)
00161                                                 strcat(toret, ".");
00162                                 }
00163                         }
00164                         else
00165                         {
00166                                 /*
00167                                  * For this, iterate over each octet,
00168                                  * then each bit within the octet.
00169                                  */
00170                                 pflen = cidr_get_pflen(block);
00171                                 if(pflen==-1)
00172                                 {
00173                                         gsh_free(toret);
00174                                         return(NULL); /* Preserve errno */
00175                                 }
00176                                 /* Special handling for forced modes */
00177                                 if(block->proto==CIDR_IPV6 && (flags & CIDR_FORCEV4))
00178                                         pflen -= 96;
00179 
00180                                 sprintf(tmpbuf, "%u",
00181                                                 (flags & CIDR_USEV6) ? pflen+96 : pflen);
00182 
00183                                 strcat(toret, tmpbuf);
00184                         }
00185                 } /* ! ONLYADDR */
00186                 
00187                 /* That's it for a v4 address, in any of our forms */
00188         }
00189         else if( (block->proto==CIDR_IPV6 && !(flags & CIDR_FORCEV4))
00190                 || (flags & CIDR_FORCEV6) )
00191         {
00192                 /* First off, creating the .ip6.arpa form is special */
00193                 if(flags & CIDR_REVERSE)
00194                 {
00195                         /*
00196                          * Build the ...ip6.arpa form.  See notes in the CIDR_REVERSE
00197                          * section of PROTO_IPV4 above for various notes.
00198                          */
00199                         sprintf(toret, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
00200                                         "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
00201                                         "%x.%x.%x.%x.%x.ip6.arpa",
00202                                         block->addr[15] & 0x0f, block->addr[15] >> 4,
00203                                         block->addr[14] & 0x0f, block->addr[14] >> 4,
00204                                         block->addr[13] & 0x0f, block->addr[13] >> 4,
00205                                         block->addr[12] & 0x0f, block->addr[12] >> 4,
00206                                         block->addr[11] & 0x0f, block->addr[11] >> 4,
00207                                         block->addr[10] & 0x0f, block->addr[10] >> 4,
00208                                         block->addr[9]  & 0x0f, block->addr[9]  >> 4,
00209                                         block->addr[8]  & 0x0f, block->addr[8]  >> 4,
00210                                         block->addr[7]  & 0x0f, block->addr[7]  >> 4,
00211                                         block->addr[6]  & 0x0f, block->addr[6]  >> 4,
00212                                         block->addr[5]  & 0x0f, block->addr[5]  >> 4,
00213                                         block->addr[4]  & 0x0f, block->addr[4]  >> 4,
00214                                         block->addr[3]  & 0x0f, block->addr[3]  >> 4,
00215                                         block->addr[2]  & 0x0f, block->addr[2]  >> 4,
00216                                         block->addr[1]  & 0x0f, block->addr[1]  >> 4,
00217                                         block->addr[0]  & 0x0f, block->addr[0]  >> 4);
00218                         return(toret);
00219                 }
00220                 /* Are we showing the address part? */
00221                 if(!(flags & CIDR_ONLYPFLEN))
00222                 {
00223                         /* It's a simple, boring, normal v6 address */
00224 
00225                         /* First, find the longest string of 0's, if there is one */
00226                         zst = zcur = -1;
00227                         zlen = zmax = 0;
00228                         for(i=0 ; i<=15 ; i+=2)
00229                         {
00230                                 if(block->addr[i]==0 && block->addr[i+1]==0)
00231                                 {
00232                                         /* This section is zero */
00233                                         if(zcur!=-1)
00234                                         {
00235                                                 /* We're already in a block of 0's */
00236                                                 zlen++;
00237                                         }
00238                                         else
00239                                         {
00240                                                 /* Starting a new block */
00241                                                 zcur = i;
00242                                                 zlen = 1;
00243                                         }
00244                                 }
00245                                 else
00246                                 {
00247                                         /* This section is non-zero */
00248                                         if(zcur!=-1)
00249                                         {
00250                                                 /*
00251                                                  * We were in 0's.  See if we set a new record,
00252                                                  * and if we did, note it and move on.
00253                                                  */
00254                                                 if(zlen > zmax)
00255                                                 {
00256                                                         zst = zcur;
00257                                                         zmax = zlen;
00258                                                 }
00259 
00260                                                 /* We're out of 0's, so reset start */
00261                                                 zcur = -1;
00262                                         }
00263                                 }
00264                         }
00265 
00266                         /*
00267                          * If zcur is !=-1, we were in 0's when the loop ended.  Redo
00268                          * the "if we have a record, update" logic.
00269                          */
00270                         if(zcur!=-1 && zlen>zmax)
00271                         {
00272                                 zst = zcur;
00273                                 zmax = zlen;
00274                         }
00275 
00276 
00277                         /*
00278                          * Now, what makes it HARD is the options we have.  To make
00279                          * some things simpler, we'll take two octets at a time for
00280                          * our run through.
00281                          */
00282                         lzer = 0;
00283                         for(i=0 ; i<=15 ; i+=2)
00284                         {
00285                                 /*
00286                                  * Start with a cheat; if this begins our already-found
00287                                  * longest block of 0's, and we're not NOCOMPACT'ing,
00288                                  * stick in a ::, increment past them, and keep on
00289                                  * playing.
00290                                  */
00291                                 if(i==zst && !(flags & CIDR_NOCOMPACT))
00292                                 {
00293                                         strcat(toret, "::");
00294                                         i += (zmax*2)-2;
00295                                         lzer = 1;
00296                                         continue;
00297                                 }
00298 
00299                                 /*
00300                                  * First, if we're not the first set, we may need a :
00301                                  * before us.  If we're not compacting, we always want
00302                                  * it.  If we ARE compacting, we want it unless the
00303                                  * previous octet was a 0 that we're minimizing.
00304                                  */
00305                                 if(i!=0 && ((flags & CIDR_NOCOMPACT) || lzer==0))
00306                                         strcat(toret, ":");
00307                                 lzer = 0; /* Reset */
00308 
00309                                 /*
00310                                  * From here on, we no longer have to worry about
00311                                  * CIDR_NOCOMPACT.
00312                                  */
00313 
00314                                 /* Combine the pair of octets into one number */
00315                                 v6sect = 0;
00316                                 v6sect |= (block->addr)[i] << 8;
00317                                 v6sect |= (block->addr)[i+1];
00318 
00319                                 /*
00320                                  * If we're being VERBOSE, use leading 0's.  Otherwise,
00321                                  * only use as many digits as we need.
00322                                  */
00323                                 if(flags & CIDR_VERBOSE)
00324                                         sprintf(tmpbuf, "%.4x", v6sect);
00325                                 else
00326                                         sprintf(tmpbuf, "%x", v6sect);
00327                                 strcat(toret, tmpbuf);
00328 
00329                                 /* And loop back around to the next 2-octet set */
00330                         } /* for(each 16-bit set) */
00331                 } /* ! ONLYPFLEN */
00332 
00333                 /* Prefix/netmask */
00334                 if(!(flags & CIDR_ONLYADDR))
00335                 {
00336                         /* Only show the / if we're not showing just the prefix */
00337                         if(!(flags & CIDR_ONLYPFLEN))
00338                                 strcat(toret, "/");
00339 
00340                         if(flags & CIDR_NETMASK)
00341                         {
00342                                 /*
00343                                  * We already wrote how to build the whole v6 form, so
00344                                  * just call ourselves recurively for this.
00345                                  */
00346                                 nmtmp = cidr_alloc();
00347                                 if(nmtmp==NULL)
00348                                 {
00349                                         gsh_free(toret);
00350                                         return(NULL); /* Preserve errno */
00351                                 }
00352                                 nmtmp->proto = block->proto;
00353                                 for(i=0 ; i<=15 ; i++)
00354                                         if(flags & CIDR_WILDCARD)
00355                                                 nmtmp->addr[i] = ~(block->mask[i]);
00356                                         else
00357                                                 nmtmp->addr[i] = block->mask[i];
00358 
00359                                 /*
00360                                  * Strip flags:
00361                                  * - CIDR_NETMASK would make us recurse forever.
00362                                  * - CIDR_ONLYPFLEN would not show the address bit, which
00363                                  *   is the part we want here.
00364                                  * Add flag CIDR_ONLYADDR because that's the bit we care
00365                                  * about.
00366                                  */
00367                                 nmflags = flags;
00368                                 nmflags &= ~(CIDR_NETMASK) & ~(CIDR_ONLYPFLEN);
00369                                 nmflags |= CIDR_ONLYADDR;
00370                                 nmstr = cidr_to_str(nmtmp, nmflags);
00371                                 cidr_free(nmtmp);
00372                                 if(nmstr==NULL)
00373                                 {
00374                                         gsh_free(toret);
00375                                         return(NULL); /* Preserve errno */
00376                                 }
00377 
00378                                 /* No need to strip the prefix, it doesn't have it */
00379 
00380                                 /* Just add it on */
00381                                 strcat(toret, nmstr);
00382                                 gsh_free(nmstr);
00383                         }
00384                         else
00385                         {
00386                                 /* Just figure the and show prefix length */
00387                                 pflen = cidr_get_pflen(block);
00388                                 if(pflen==-1)
00389                                 {
00390                                         gsh_free(toret);
00391                                         return(NULL); /* Preserve errno */
00392                                 }
00393                                 /* Special handling for forced modes */
00394                                 if(block->proto==CIDR_IPV4 && (flags & CIDR_FORCEV6))
00395                                         pflen += 96;
00396 
00397                                 sprintf(tmpbuf, "%u", pflen);
00398                                 strcat(toret, tmpbuf);
00399                         }
00400                 } /* ! ONLYADDR */
00401         }
00402         else
00403         {
00404                 /* Well, *I* dunno what the fuck it is */
00405                 gsh_free(toret);
00406                 errno = ENOENT; /* Bad choice of errno */
00407                 return(NULL);
00408         }
00409 
00410         /* Give back the string */
00411         return(toret);
00412 }