nfs-ganesha 1.4
|
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 }