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

背景

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
...
# mv webcam_server.h{,.ORG}
# cp -a webcam_server.h{.ORG,}
# vi webcam_server.h
...
# mv client.c{,.ORG}
# cp -a client.c{.ORG,}
# vi client.c
...
# mv client.h{,.ORG}
# cp -a client.h{.ORG,}
# vi client.h
...
#

3.ビルドして動作確認

# 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
tcp        0      0 *:8888                  *:*                     LISTEN
tcp6       0      0 [::]:8888               [::]:*                  LISTEN
# tail /var/log/webcam_server.log
...
2009-10-04 19:31:50 [/dev/video0] webcam_server started
2009-10-04 19:32:14 [/dev/video0] 2002:dbcc:d803:12:cde5:1bd6:dffc:7c87 connected via HTTP
2009-10-04 19:32:14 [/dev/video0] 2002:dbcc:d803:12:cde5:1bd6:dffc:7c87 disconnected, 0 seconds, 10386 bytes, 10.14 Kbytes/second, 1 frames, 1.00 fps
2009-10-04 19:58:14 [/dev/video0] touch.mmizuki.dyndns.org connected via HTTP
2009-10-04 19:58:14 [/dev/video0] touch.mmizuki.dyndns.org disconnected, 0 seconds, 8701 bytes, 8.50 Kbytes/second, 1 frames, 1.00 fps
...
#

4.さぁ、Let's enjoy

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

Your own risk でね

--- webcam-server-0.50.orig/src/client.c
+++ webcam-server-0.50/src/client.c
@@ -22,6 +22,9 @@
 #include <unistd.h>
 #include <semaphore.h>
 #include <linux/videodev.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <string.h>

 #include "client.h"
 #include "webcam_server.h"
@@ -138,7 +141,15 @@
        char logmsg[512];
        if(match_admin_password(request, cam->o.admin_pw) < 0)
        {
-               sprintf(logmsg, "%s attempted admin access with bad password\n", inet_ntoa(con->remote_addr.sin_addr));
+               char host[BUFSIZ];
+               int rc;
+               if((rc = getnameinfo((struct sockaddr *)&con->remote_addr, sizeof(con->remote_addr),
+                       host, sizeof(host), NULL, 0, 0)) != 0)
+               {
+                       fprintf(stderr, "getnameinfo(): %s\n", gai_strerror(rc));
+                       strncpy(host, "Unknown Remote", sizeof(host));
+               }
+               sprintf(logmsg, "%s attempted admin access with bad password\n", host);
                log(cam, logmsg);
                return -1;
        }
@@ -251,6 +262,8 @@
                /* do the following only once per connection */
                if(con->start == 0)
                {
+                       char host[BUFSIZ];
+                       int rc;
                        char *client = NULL;
                        /* someone has connected, inform the grabber thread */
                        sem_post(&cam->sem_con);
@@ -274,7 +287,13 @@
                                        break;
                        }

-                       sprintf(logmsg, "%s connected via %s\n", inet_ntoa(con->remote_addr.sin_addr), client);
+                       if((rc = getnameinfo((struct sockaddr *)&con->remote_addr, sizeof(con->remote_addr),
+                               host, sizeof(host), NULL, 0, 0)) != 0)
+                       {
+                               fprintf(stderr, "getnameinfo(): %s\n", gai_strerror(rc));
+                               strncpy(host, "Unknown Remote", sizeof(host));
+                       }
+                       sprintf(logmsg, "%s connected via %s\n", host, client);
                        log(cam, logmsg);
                }

@@ -354,12 +373,20 @@
        }
        if(con->client_type > 0)
        {
+               char host[BUFSIZ];
+               int rc;

                /* get end time */
                time(&con->end);

+               if((rc = getnameinfo((struct sockaddr *)&con->remote_addr, sizeof(con->remote_addr),
+                       host, sizeof(host), NULL, 0, 0)) != 0)
+               {
+                       fprintf(stderr, "getnameinfo(): %s\n", gai_strerror(rc));
+                       strncpy(host, "Unknown Remote", sizeof(host));
+               }
                sprintf(logmsg, "%s disconnected, %d seconds, %ld bytes, %.2f Kbytes/second, %ld frames, %.2f fps\n",
-                       inet_ntoa(con->remote_addr.sin_addr),           /* address */
+                       host,                                           /* address */
                        (int)(con->end - con->start),                   /* seconds */
                        con->total_bytes,                               /* bytes */
                        (float)((con->total_bytes/1024.0)/((con->end - con->start) ? con->end - con->start : 1)),/* Kbytes/sec */
--- webcam-server-0.50.orig/src/webcam_server.h
+++ webcam-server-0.50/src/webcam_server.h
@@ -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
--- webcam-server-0.50.orig/src/client.h
+++ webcam-server-0.50/src/client.h
@@ -18,7 +18,7 @@
 struct connection
 {
        int socketfd;
-       struct sockaddr_in remote_addr;
+       struct sockaddr_storage remote_addr;
        u_long total_bytes;
        u_long total_frames;
        u_long id_last;
--- webcam-server-0.50.orig/src/webcam_server.c
+++ webcam-server-0.50/src/webcam_server.c
@@ -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;
 }

webcam-server IPv6 (2) (最終更新日時 2013-07-21 19:34:28 更新者 mmizuki)