webcam-server の IPv6 対応 その1について、ここに記述してください。

背景

1.お勉強

2.とりあえず対応させてみる

# cd /usr/src
# apt-get source webcam-server
...
# cd webcam-server-0.50/src
# mv webcam_server.c{,.ORG}
# cp -a webcam_server.c{.ORG,}
# vi webcam_server.c
...
# diff -u webcam_server.c{.ORG,}
--- webcam_server.c.ORG 2004-09-28 15:40:58.000000000 +0900
+++ webcam_server.c     2009-10-02 22:59:26.000000000 +0900
@@ -25,6 +25,9 @@
 #include <linux/videodev.h>
 #include <semaphore.h>
 #include <sys/ioctl.h>
+#if 1  /* IPv6 ready */
+#include <netdb.h>
+#endif /* 1 */

 #include "webcam_server.h"
 #include "grabber.h"
@@ -124,6 +127,54 @@
 int create_and_listen(int port)
 {
        int sockfd, yes=1;
+#if 1  /* IPv6 ready (dual stack) */
+       int err;
+       struct addrinfo hints;
+       struct addrinfo* res = NULL;
+       struct addrinfo* ai;
+       char service[BUFSIZ];
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_INET6;    // AF_INET6は、IPv4/IPv6両方を受け付ける  。
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_flags = AI_PASSIVE;   // bind()する場合は指定する。
+
+       // node = NULLのとき、INADDR_ANY, IN6ADDR_ANY_INIT に相当。
+       sprintf(service, "%d\0", port);
+       if((err = getaddrinfo(NULL, service, &hints, &res)) != 0)
+       {
+               printf("getaddrinfo(): %s\n", gai_strerror(err));
+               return -1;
+       }
+
+       ai = res;
+       if((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
+       {
+               perror("socket");
+               return -1;
+       }
+
+       if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0)
+       {
+               perror("setsockopt");
+               return -1;
+       }
+
+       if(bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
+       {
+               perror("bind");
+               return -1;
+       }
+
+       if(listen(sockfd, 5) < 0)
+       {
+               perror("listen");
+               return -1;
+       }
+
+       freeaddrinfo(res);
+
+#else  /* IPv4 only */
        struct sockaddr_in addr;

        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
@@ -155,6 +206,7 @@
                perror("listen");
                return -1;
        }
+#endif /* 1 */

        return sockfd;
 }
@@ -540,7 +592,11 @@
 int main(int argc, char *argv[])
 {
        int sockfd=0, clientfd;
+#if 1  /* IPv6 ready */
+       struct sockaddr_storage remote_addr;  // sockaddr_in 型ではない。
+#else  /* IPv4 only */
        struct sockaddr_in remote_addr;
+#endif /* 1 */
        socklen_t addr_len;
        pthread_t pt;
        struct caminfo *cam;
#

# cd /usr/src/webcam-server-0.50
# ./configure
...
# make
...
# mv /usr/bin/webcam-server{,.ORG}
# cp -a src/webcam_server /usr/bin/webcam-server
# /usr/bin/webcam-server -d /dev/video0 -s
# netstat -l | grep 8888
tcp6       0      0 [::]:8888               [::]:*                  LISTEN
#

3.ちゃんと IPv6 に対応させる

# cd /usr/src/webcam-server-0.50/src
# mv webcam_server.c{,.ORG}
# cp -a webcam_server.c{.ORG,}
# vi webcam_server.c
...
# diff -u webcam_server.c{.ORG,}
--- webcam-server-0.50.ORG/src/webcam_server.c  2004-09-28 15:40:58.000000000 +0900
+++ webcam-server-0.50/src/webcam_server.c      2009-10-04 03:33:40.000000000 +0900
@@ -25,6 +25,11 @@
 #include <linux/videodev.h>
 #include <semaphore.h>
 #include <sys/ioctl.h>
+// IPv6 extent
+#include <errno.h>
+#include <sys/select.h>
+#include <netdb.h>
+//

 #include "webcam_server.h"
 #include "grabber.h"
@@ -121,42 +126,75 @@
   - creates a server socket and listens
   - its a seperate function to clean up main
   -----------------------------------------------------------*/
-int create_and_listen(int port)
+int create_and_listen(int port, int *sockfd, int nsockfd)
 {
-       int sockfd, yes=1;
-       struct sockaddr_in addr;
-
-       if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
-       {
-               perror("socket");
-               return -1;
-       }
-
-       memset(&addr, 0, sizeof(addr));
-
-       addr.sin_family = AF_INET;
-       addr.sin_port = htons(port);
-       addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
-       if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0)
-       {
-               perror("setsockopt");
-               return -1;
-       }
-
-       if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0)
+       int yes=1;
+       int num_listen_socks = 0;
+       int err;
+       struct addrinfo hints;
+       struct addrinfo* res = NULL;
+       struct addrinfo* ai;
+       char service[BUFSIZ];
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_UNSPEC;    // IPv4 or IPv6
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_flags = AI_PASSIVE;    // to bind()
+
+       // node = NULL means INADDR_ANY, IN6ADDR_ANY_INIT
+       sprintf(service, "%d", port);
+       if((err = getaddrinfo(NULL, service, &hints, &res)) != 0)
        {
-               perror("bind");
+               printf("getaddrinfo(): %s\n", gai_strerror(err));
                return -1;
        }
-
-       if(listen(sockfd, 5) < 0)
+
+       for(ai = res; ai; ai = ai->ai_next)
        {
-               perror("listen");
-               return -1;
+               if((*sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
+               {
+                       perror("socket");
+                       return -1;
+               }
+
+               if(setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0)
+               {
+                       perror("setsockopt");
+                       return -1;
+               }
+
+#ifdef IPV6_V6ONLY
+               // IPv6 socket accepts from IPv6 only
+               if(ai->ai_family == AF_INET6)
+               {
+                       if(setsockopt(*sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(int)) < 0)
+                       {
+                               perror("setsockopt");
+                               return -1;
+                       }
+               }
+#endif  /* IPV6_V6ONLY */
+
+               if(bind(*sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
+               {
+                       perror("bind");
+                       return -1;
+               }
+
+               if(listen(*sockfd, 5) < 0)
+               {
+                       perror("listen");
+                       return -1;
+               }
+
+               sockfd++;
+               num_listen_socks++;
+               if(num_listen_socks >= nsockfd)
+                       break;
        }
-
-       return sockfd;
+       freeaddrinfo(res);
+
+       return num_listen_socks;
 }

 /*-----------------------------------------------------------
@@ -539,8 +577,12 @@

 int main(int argc, char *argv[])
 {
-       int sockfd=0, clientfd;
-       struct sockaddr_in remote_addr;
+#define MAX_LISTEN_SOCKS       16
+       int sockfd[MAX_LISTEN_SOCKS], clientfd;
+       int nsockfd=0, smax;
+       fd_set rfd, rfd_init;
+       int i;
+       struct sockaddr_storage remote_addr;    // does NOT sockaddr_in
        socklen_t addr_len;
        pthread_t pt;
        struct caminfo *cam;
@@ -610,8 +652,7 @@
        if(!cam->o.test_fps)
        {
                /* create listening socket */
-               sockfd = create_and_listen(cam->o.port);
-               if(sockfd < 1)
+               if((nsockfd = create_and_listen(cam->o.port, sockfd, MAX_LISTEN_SOCKS)) < 1)
                        exit(1);

                /* go into daemon mode if told to */
@@ -734,23 +775,55 @@

        log(cam, "webcam_server started\n");

-       addr_len = sizeof(remote_addr);
+       FD_ZERO(&rfd_init);
+       smax = -1;
+       for(i = 0; i < nsockfd; i++)
+       {
+               FD_SET(sockfd[i], &rfd_init);
+               if(sockfd[i] > smax)
+                       smax = sockfd[i];
+       }

-       while((clientfd = accept(sockfd, (struct sockaddr *)&remote_addr, &addr_len)) > 0)
+       for(;;)
        {
-               /* con is free'd at end of handle_connection */
-               con = (struct connection*)malloc(sizeof(struct connection));
-               con->cam = cam;
+               rfd = rfd_init;

-               /* get connection info */
-               con->socketfd = clientfd;
-               memcpy(&con->remote_addr, &remote_addr, addr_len);
+               if(select(smax + 1, &rfd, NULL, NULL, NULL) < 0)
+               {
+                       if(errno == EINTR)
+                               continue;
+                       perror("select");
+                       exit(1);
+               }
+
+               for(i = 0; i < nsockfd; i++)
+               {
+                       if(FD_ISSET(sockfd[i], &rfd))
+                       {
+                               addr_len = sizeof(remote_addr);

-               /* handle the connection */
-               pthread_create(&pt, NULL, (void*)handle_connection, (void*)con);
-               pthread_detach(pt);
+                               if((clientfd = accept(sockfd[i], (struct sockaddr *)&remote_addr, &addr_len)) < 0)
+                               {
+                                       perror("accept");
+                                       exit(1);
+                               }
+
+                               /* con is free'd at end of handle_connection */
+                               con = (struct connection*)malloc(sizeof(struct connection));
+                               con->cam = cam;
+
+                               /* get connection info */
+                               con->socketfd = clientfd;
+                               memcpy(&con->remote_addr, &remote_addr, addr_len);
+
+                               /* handle the connection */
+                               pthread_create(&pt, NULL, (void*)handle_connection, (void*)con);
+                               pthread_detach(pt);
+                       }
+               }
        }

-       close(sockfd);
+       for(i = 0; i < nsockfd; i++)
+               close(sockfd[i]);
        return 0;
 }
# mv webcam_server.h{,.ORG}
# cp -a webcam_server.h{.ORG,}
# vi webcam_server.h
...
# diff -u webcam_server.h{.ORG,}
--- webcam-server-0.50.ORG/src/webcam_server.h  2004-09-28 15:40:58.000000000 +0900
+++ webcam-server-0.50/src/webcam_server.h      2009-10-04 02:24:45.000000000 +0900
@@ -97,6 +97,6 @@
 void usage();
 int parse_args(struct caminfo *cam, int argc, char *argv[]);
 void log(struct caminfo *cam, char *text);
-int create_and_listen(int port);
+int create_and_listen(int port, int *sockfd, int nsockfd);

 #endif
# cd ..
# ./configure
# make
# mv /usr/bin/webcam-server{,.ORG}
# cp -a src/webcam_server /usr/bin/webcam-server
#

4.さぁ、Let's enjoy

お手軽だしこれでいいやという方向けに、patch と binaries も置いておきます

Your own risk でね

webcam-server IPv6 (1) (最終更新日時 2013-07-21 19:31:52 更新者 mmizuki)