1/* @(#) interface/address conversion */ 2 3#include <sys/socket.h> 4#include <sys/ioctl.h> 5#include <sys/types.h> 6#include <string.h> 7#include <unistd.h> 8#include <errno.h> 9#include <net/if.h> 10#include <stdlib.h> 11#include <netinet/in.h> 12#include <arpa/inet.h> 13 14#include <assert.h> 15#include <limits.h> 16 17#include "osdef.h" 18#include "ifaddr.h" 19 20/* DEBUG */ 21#include <stdio.h> 22/* DEBUG */ 23 24 25/* check if ifr contains info on the desired ifname 26 */ 27static int 28chkifr( const struct ifreq* ifr, const char* ifname, 29 const size_t addrlen, size_t* offset ) 30{ 31 size_t sa_len = 0; 32 33 assert(ifr && ifname && offset); 34 35#ifdef NO_SOCKADDR_SA_LEN 36 switch( ifr->ifr_addr.sa_family ) 37 { 38 #ifndef NO_INET6_SUPPORT 39 case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break; 40 #endif 41 case AF_INET: sa_len = sizeof(struct sockaddr); break; 42 default: sa_len = 0; break; 43 } 44#else 45 sa_len = ifr->ifr_addr.sa_len; 46#endif 47 48 if( sa_len > 0 ) { 49 if ( (ifr->ifr_addr.sa_family == AF_INET) && 50 (0 == strncmp(ifname, ifr->ifr_name, sizeof(struct ifreq))) && 51 (addrlen >= sa_len) ) { 52 *offset = sa_len; 53 return 0; 54 } 55 } 56 57#if defined(__linux) 58 *offset = sizeof(*ifr); 59#else 60 *offset = (sa_len + sizeof( ifr->ifr_name )); 61 /* the above is per R. Stevens' book and not working on 64-bit Linux */ 62#endif 63 64 return -1; 65} 66 67 68 69/* retrieve IPv4 address of the given network interface 70 */ 71int 72if2addr( const char* ifname, 73 struct sockaddr *addr, size_t addrlen ) 74{ 75 int rc, sockfd; 76 char *buf, *rec; 77 size_t buflen, offset; 78 int last_len; 79 struct ifconf ifc; 80 struct ifreq ifr; 81 82 int maxi = 50; 83 FILE *fp_ifc = NULL; 84 size_t nsz = 0; 85 86 static size_t IFC_TABLE_SIZE; 87 88 static const size_t IFC_ENTRIES = 32; 89 static const size_t MAX_IFCBUF_SIZE = (1024 * 256); 90 91 IFC_TABLE_SIZE = sizeof(struct ifreq) * IFC_ENTRIES; 92 93 assert( ifname && addr && addrlen ); 94 rc = 0; 95 96 /* acquire the list of network interfaces */ 97 98 sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); 99 if( -1 == sockfd ) return -1; 100 101 buf = NULL; buflen = IFC_TABLE_SIZE; last_len = 0; 102 for( ; buflen < MAX_IFCBUF_SIZE; buflen += IFC_TABLE_SIZE ) { 103 if( NULL == (buf = malloc( buflen )) ) { 104 rc = -1; 105 break; 106 } 107 108 ifc.ifc_len = buflen; 109 ifc.ifc_buf = buf; 110 if( ioctl( sockfd, SIOCGIFCONF, &ifc ) < 0 ) { 111 if( (EINVAL != errno) || (last_len != 0) ) { 112 rc = errno; 113 break; 114 } 115 } 116 else { 117 if( ifc.ifc_len == last_len ) 118 break; 119 else 120 last_len = ifc.ifc_len; 121 } 122 123 free( buf ); 124 buf = NULL; 125 } /* for */ 126 127 (void) close( sockfd ); 128 if( buflen > MAX_IFCBUF_SIZE ) rc = -1; 129 130 if( 0 != rc ) { 131 if( NULL != buf ) free( buf ); 132 return rc; 133 } 134 135 assert( ifc.ifc_buf ); 136 /* DEBUG */ 137 fp_ifc = fopen("ifcbuf.dat","w"); 138 if (NULL==fp_ifc) { 139 perror("fopen ifcbuf"); 140 exit(1); 141 } 142 nsz = fwrite(ifc.ifc_buf, ifc.ifc_len, 1, fp_ifc ); 143 (void) fprintf(stderr, "%ld record(s) wtitten to ifcbuf.dat\n", 144 (long)nsz ); 145 fclose(fp_ifc); 146 /* DEBUG */ 147 148 /* look for ifname in the list */ 149 150 for( rec = ifc.ifc_buf; rec < (ifc.ifc_buf + ifc.ifc_len); ) { 151 (void) memcpy( &ifr, rec, sizeof(struct ifreq) ); 152 153 (void) fprintf(stderr, "DEBUG1: rec=%p, max=%p\n", 154 (void*)rec, (void*)(ifc.ifc_buf + ifc.ifc_len) ); 155 if( --maxi <= 0 ) break; 156 157 offset = 0; 158 rc = chkifr( &ifr, ifname, addrlen, &offset ); 159 if ( 0 == rc ) { 160 (void) memcpy( addr, &(ifr.ifr_addr), offset ); 161 break; 162 } 163 164 if( 0 == offset ) break; 165 rec += offset; 166 } /* for */ 167 168 if( rec >= (buf + ifc.ifc_len) ) { 169 rc = -1; 170 } 171 172 free( buf ); 173 return rc; 174} 175 176 177/* convert input parameter into an IPv4-address string 178 */ 179int 180get_ipv4_address( const char* s, char* buf, size_t len ) 181{ 182 struct sockaddr_in saddr; 183 int rc = 0; 184 185 assert( s && buf && len ); 186 187 if( 1 == inet_aton(s, &(saddr.sin_addr)) ) { 188 (void) strncpy( buf, s, len ); 189 } 190 else { 191 rc = if2addr( s, (struct sockaddr*)&saddr, sizeof(saddr) ); 192 if( 0 != rc ) return rc; 193 194 (void) strncpy( buf, inet_ntoa(saddr.sin_addr), len ); 195 } 196 197 buf[ len - 1 ] = 0; 198 return rc; 199} 200 201 202/* split input string into IP address and port 203 */ 204int 205get_addrport( const char* s, char* addr, size_t len, int* port ) 206{ 207 struct sockaddr_in saddr; 208 size_t i = 0; 209 int iport = 0; 210 211 static const int ERR_NOPORT = -1; 212 static const int ERR_BADADDR = -2; 213 static const int ERR_BADPORT = -3; 214 static const int ERR_OVERFLOW = -4; 215 216 assert( s && addr && len && port ); 217 218 for( i = 0; (i < len) && s[i] && (':' != s[i]); ++i ) 219 addr[ i ] = s[ i ]; 220 if( i >= len ) 221 return ERR_OVERFLOW; 222 else 223 addr[i] = '\0'; 224 225 /* IP address is not followed by port */ 226 if( ':' != s[ i ] ) return ERR_NOPORT; 227 228 if( 1 != inet_aton( addr, &(saddr.sin_addr)) ) 229 return ERR_BADADDR; 230 231 ++i; 232 if( i >= len || !s[i] ) return ERR_NOPORT; 233 234 errno = 0; 235 iport = atoi( s + i ); 236 if( errno || (iport <= 0) || (iport > (int)USHRT_MAX) ) 237 return ERR_BADPORT; 238 239 *port = iport; 240 return 0; 241} 242 243 244/* __EOF__ */ 245 246