Index: memcached.c =================================================================== --- memcached.c (revision 572) +++ memcached.c (working copy) @@ -101,6 +101,7 @@ /** exported globals **/ struct stats stats; struct settings settings; +bool ipv6=false; /** file scope variables **/ static item **todelete = 0; @@ -167,7 +168,6 @@ static void settings_init(void) { settings.port = 11211; settings.udpport = 0; - settings.interf.s_addr = htonl(INADDR_ANY); settings.maxbytes = 67108864; /* default is 64MB: (64 * 1024 * 1024) */ settings.maxconns = 1024; /* to limit connections-related memory to about 5MB */ settings.verbose = 0; @@ -1646,9 +1646,17 @@ assert(c != NULL); - c->request_addr_size = sizeof(c->request_addr); +if(ipv6) { + c->request_addr_size = sizeof(struct in6_addr); res = recvfrom(c->sfd, c->rbuf, c->rsize, - 0, &c->request_addr, &c->request_addr_size); + 0, ( struct sockaddr *)&c->request_addr, &c->request_addr_size); + } + else { + c->request_addr_size = sizeof(struct in_addr); + res = recvfrom(c->sfd, c->rbuf, c->rsize, + 0, ( struct sockaddr *)&c->request_addr, &c->request_addr_size); + } + if (res > 8) { unsigned char *buf = (unsigned char *)c->rbuf; STATS_LOCK(); @@ -1713,7 +1721,11 @@ * is this done for every command? presumably for UDP * mode. */ if (!settings.socketpath) { - c->request_addr_size = sizeof(c->request_addr); + if(ipv6) { + c->request_addr_size = sizeof(struct in6_addr); + } else { + c->request_addr_size = sizeof(struct in_addr); + } } else { c->request_addr_size = 0; } @@ -1846,7 +1858,7 @@ bool stop = false; int sfd, flags = 1; socklen_t addrlen; - struct sockaddr addr; + struct sockaddr_storage addr; int res; assert(c != NULL); @@ -1855,8 +1867,16 @@ switch(c->state) { case conn_listening: - addrlen = sizeof(addr); - if ((sfd = accept(c->sfd, &addr, &addrlen)) == -1) { + + if(ipv6) + { + addrlen = sizeof(struct in6_addr); + } + else { + addrlen = sizeof(struct in_addr); + } + + if ((sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen)) == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { /* these are transient, so don't log anything */ stop = true; @@ -2082,10 +2102,17 @@ int sfd; int flags; - if ((sfd = socket(AF_INET, is_udp ? SOCK_DGRAM : SOCK_STREAM, 0)) == -1) { - perror("socket()"); - return -1; - } +if(ipv6) { + if ((sfd = socket(AF_INET6, is_udp ? SOCK_DGRAM : SOCK_STREAM, 0)) == -1) { + perror("socket()"); + return -1; + } + } else { + if ((sfd = socket(AF_INET, is_udp ? SOCK_DGRAM : SOCK_STREAM, 0)) == -1) { + perror("socket()"); + return -1; + } + } if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 || fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) { @@ -2131,11 +2158,10 @@ fprintf(stderr, "<%d send buffer was %d, now %d\n", sfd, old_size, last_good); } - static int server_socket(const int port, const bool is_udp) { int sfd; struct linger ling = {0, 0}; - struct sockaddr_in addr; + struct sockaddr_storage addr; int flags =1; if ((sfd = new_socket(is_udp)) == -1) { @@ -2157,13 +2183,29 @@ */ memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr = settings.interf; - if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - perror("bind()"); - close(sfd); - return -1; +if(ipv6) { + struct sockaddr_in6 *addrp=(struct sockaddr_in6 *) &addr; + + addrp->sin6_family = AF_INET6; + addrp->sin6_port = htons(port); + + memcpy(&(addrp->sin6_addr),&(settings.interf),sizeof(struct in6_addr)); + + if (bind(sfd, (struct sockaddr *)addrp, sizeof(struct sockaddr_in6)) == -1) { + perror("bind()"); + close(sfd); + return -1; + } + } else { + struct sockaddr_in *addrp=(struct sockaddr_in *) &addr; + addrp->sin_family = AF_INET; + addrp->sin_port = htons(port); + memcpy(&(addrp->sin_addr),&(settings.interf),sizeof(struct in_addr)); + if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1) { + perror("bind()"); + close(sfd); + return -1; + } } if (!is_udp && listen(sfd, 1024) == -1) { perror("listen()"); @@ -2460,7 +2502,7 @@ int main (int argc, char **argv) { int c; - struct in_addr addr; + struct in_any_addr addr; bool lock_memory = false; bool daemonize = false; int maxcore = 0; @@ -2469,6 +2511,7 @@ struct passwd *pw; struct sigaction sa; struct rlimit rlim; + char *opt_addr = NULL; /* handle SIGINT */ signal(SIGINT, sig_handler); @@ -2480,7 +2523,7 @@ setbuf(stderr, NULL); /* process arguments */ - while ((c = getopt(argc, argv, "bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:")) != -1) { + while ((c = getopt(argc, argv, "bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:D:6")) != -1) { switch (c) { case 'U': settings.udpport = atoi(optarg); @@ -2515,13 +2558,10 @@ case 'v': settings.verbose++; break; - case 'l': - if (inet_pton(AF_INET, optarg, &addr) <= 0) { - fprintf(stderr, "Illegal address: %s\n", optarg); - return 1; - } else { - settings.interf = addr; - } + case 'l': + opt_addr=malloc(strlen(optarg)+1); + memset(opt_addr,0,strlen(optarg)+1); + strcat(opt_addr,optarg); break; case 'd': daemonize = true; @@ -2564,12 +2604,69 @@ settings.prefix_delimiter = optarg[0]; settings.detail_enabled = 1; break; + case '6': + ipv6=true; + break; default: fprintf(stderr, "Illegal argument \"%c\"\n", c); return 1; } } + + // do default address initialization that was done in settings_init + if(ipv6) { + memcpy(&settings.interf.s_addr,&in6addr_any,sizeof(struct in6_addr)); + } else { + struct in_addr *addrp = (struct in_addr *) settings.interf.s_addr; + struct in_addr any; + any.s_addr=htonl(INADDR_ANY); + memcpy(addrp,&any,sizeof(struct in_addr)); + } + + + if(opt_addr) { + if(ipv6) { + if (inet_pton(AF_INET6, opt_addr, (void *)&addr) <= 0) { + char *addr_buff; + int addr_buff_len; + struct in6_addr temp; + + // this is not valid IPv6 address, check if this is an IPv4 address + if (inet_pton(AF_INET, opt_addr, (void *)&temp) <= 0) { + fprintf(stderr, "Illegal address: %s\n", opt_addr); + return 1; + } + + addr_buff_len=10+strlen(opt_addr); + addr_buff=malloc(10+strlen(opt_addr)); + + fprintf(stderr, "converting ipv4 address format: %s\n", opt_addr); + + strcat(addr_buff,"::ffff:"); + strcat(addr_buff,opt_addr); + + fprintf(stderr, "to ipv6 address format: %s\n", addr_buff); + + if (inet_pton(AF_INET6, addr_buff, (void *)&addr) <= 0) { + fprintf(stderr, "Illegal address: %s\n", opt_addr); + return 1; + } + + free(addr_buff); + } + memcpy(&(settings.interf),&addr,sizeof(struct in6_addr)); + } else { + + if (inet_pton(AF_INET, opt_addr, (void *)&addr) <= 0) { + fprintf(stderr, "Illegal address: %s\n", opt_addr); + return 1; + } else { + memcpy(&(settings.interf),&addr,sizeof(struct in_addr)); + } + } + } + if (maxcore != 0) { struct rlimit rlim_new; /* Index: memcached.h =================================================================== --- memcached.h (revision 572) +++ memcached.h (working copy) @@ -69,12 +69,17 @@ #define MAX_VERBOSITY_LEVEL 2 +struct in_any_addr { + unsigned char s_addr[sizeof(struct in6_addr)]; + }; + + struct settings { size_t maxbytes; int maxconns; int port; int udpport; - struct in_addr interf; + struct in_any_addr interf; int verbose; rel_time_t oldest_live; /* ignore existing items older than this */ bool managed; /* if 1, a tracker manages virtual buckets */ @@ -89,6 +94,7 @@ extern struct stats stats; extern struct settings settings; +extern bool ipv6; #define ITEM_LINKED 1 #define ITEM_DELETED 2 @@ -190,7 +196,7 @@ /* data for UDP clients */ bool udp; /* is this is a UDP "connection" */ int request_id; /* Incoming UDP request ID, if this is a UDP "connection" */ - struct sockaddr request_addr; /* Who sent the most recent request */ + struct in_any_addr request_addr; /* Who sent the most recent request */ socklen_t request_addr_size; unsigned char *hdrbuf; /* udp packet headers */ int hdrsize; /* number of headers' worth of space is allocated */