summaryrefslogtreecommitdiffstats
path: root/hw4/chttpd/chttpd-socket.c
blob: 7fd0efcda38a6e3532d7c32db312bb4fb1fd5528 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* b01902062 藍挺瑋 */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "chttpd-socket.h"

#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

#define SOCKADDR(x)  ((struct sockaddr*)(x))
#define SOCKLEN(x)   ((socklen_t)(x))

int chttpd_socket_new_unix (const char* path,
    struct sockaddr_un* saddr, socklen_t* saddrlen) {

    struct sockaddr_un addr;
    socklen_t addrlen = sizeof (addr);
    size_t pathlen;

    memset (&addr, 0, sizeof (addr));
    pathlen = sizeof (addr) - offsetof (struct sockaddr_un, sun_path) - 1;
    addr.sun_family = AF_UNIX;
    strncpy (addr.sun_path, path, pathlen);

    int sockfd = socket (AF_UNIX, SOCK_STREAM, 0);
    if (sockfd < 0) {
        return CHTTPD_SOCKET_NEW_ERROR_SOCKET;
    }
    if (bind (sockfd, SOCKADDR (&addr), addrlen) < 0) {
        close (sockfd);
        return CHTTPD_SOCKET_NEW_ERROR_BIND;
    }
    if (listen (sockfd, SOMAXCONN) < 0) {
        close (sockfd);
        return CHTTPD_SOCKET_NEW_ERROR_LISTEN;
    }

    if (saddr != NULL) {
        *saddr = addr;
    }
    if (saddrlen != NULL) {
        *saddrlen = addrlen;
    }

    return sockfd;
}

int chttpd_socket_new_inet (const char* host, const char* service, int domain,
    struct sockaddr* saddr, socklen_t* saddrlen, int* error) {

    if (service == NULL) {
        service = "http";
    }
    if (domain != AF_INET && domain != AF_INET6) {
        domain = AF_UNSPEC;
    }

    struct addrinfo hints, *result;
    memset (&hints, 0, sizeof (hints));
    hints.ai_family = domain;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;

    int gai_error = getaddrinfo (host, service, &hints, &result);
    if (gai_error != 0) {
        if (error != NULL) {
            *error = gai_error;
        }
        return CHTTPD_SOCKET_NEW_ERROR_GETADDRINFO;
    }

    int sockfd = -1;
    int bindrv = -1;
    for (struct addrinfo* iter = result; iter != NULL; iter = iter->ai_next) {
        sockfd = socket (iter->ai_family, SOCK_STREAM, 0);
        if (sockfd < 0) {
            continue;
        }

        if (iter->ai_family == AF_INET6) {
            setsockopt (sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
                &(int){ 1 }, sizeof (int));
        }

        bindrv = bind (sockfd, iter->ai_addr, iter->ai_addrlen);
        if (bindrv < 0) {
            close (sockfd);
        } else if (listen (sockfd, SOMAXCONN) < 0) {
                close (sockfd);
        } else {
            if (saddr != NULL) {
                switch (iter->ai_family) {
                    case AF_INET:
                        *(struct sockaddr_in*)(saddr) =
                            *(struct sockaddr_in*)(iter->ai_addr);
                        break;
                    case AF_INET6:
                        *(struct sockaddr_in6*)(saddr) =
                            *(struct sockaddr_in6*)(iter->ai_addr);
                        break;
                    default:
                        freeaddrinfo (result);
                        return CHTTPD_SOCKET_NEW_ERROR_UNEXPECTED;
                }
            }
            if (saddrlen != NULL) {
                *saddrlen = iter->ai_addrlen;
            }
            freeaddrinfo (result);
            return sockfd;
        }
    }

    freeaddrinfo (result);
    if (sockfd < 0) {
        return CHTTPD_SOCKET_NEW_ERROR_SOCKET;
    }
    if (bindrv < 0) {
        return CHTTPD_SOCKET_NEW_ERROR_BIND;
    }

    return CHTTPD_SOCKET_NEW_ERROR_LISTEN;
}