summaryrefslogtreecommitdiffstats
path: root/hw1/server.c
blob: 3794847920f2c69cf9787dc20009312a3c95a0b9 (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
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "server.h"

#include "socktool.h"
#include "connection.h"

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

int ras_server_init (
        RasServer* server, RasConnPerm perm, int domain,
        const char* log_file, int id) {

    /* chain up to parent constructor */
    int rval =
        ras_conn_init (RAS_CONN (server), perm, domain, "ras-server", log_file, id);
    if (rval < 0) {
        return rval;
    }

    return 0;
}

void ras_server_destroy (RasServer* server) {
    if (RAS_CONN (server)->domain == AF_LOCAL) {
        unlink (SOCKADDR_UN (&server->addr)->sun_path);
    }

    /* chain up to parent destructor */
    ras_conn_destroy (RAS_CONN (server));
}

int ras_server_listen (RasServer* server, const char* addr, int arg) {
    /* domain == AC_LOCAL => arg == mode
     * domain == AC_INET  => arg == port
     * domain == AC_INET6 => arg == port */
    int len, fd;
    struct sockaddr_storage sock;
    socklen_t socklen;
    memset (&sock, 0, sizeof (sock));
    switch (RAS_CONN (server)->domain) {
        case AF_LOCAL:
            socklen = sizeof (struct sockaddr_un);
            len = socklen - offsetof (struct sockaddr_un, sun_path) - 1;
            SOCKADDR_UN (&sock)->sun_family = AF_LOCAL;
            strncpy (SOCKADDR_UN (&sock)->sun_path, addr, len);
            ras_server_log (server, "addr = %s, mode = %o", addr, arg);
            break;
        case AF_INET:
            socklen = sizeof (struct sockaddr_in);
            SOCKADDR_IN (&sock)->sin_family = AF_INET;
            SOCKADDR_IN (&sock)->sin_port = htons (arg);
            if (addr == NULL) {
                SOCKADDR_IN (&sock)->sin_addr.s_addr = htonl (INADDR_ANY);
                addr = "any";
            } else {
                if (inet_pton (AF_INET, addr, &(SOCKADDR_IN (&sock)->sin_addr)) <= 0) {
                    ras_server_log (server, "unknown IPv4 address: %s", addr);
                    return -1;
                }
            }
            ras_server_log (server, "addr = %s, port = %d", addr, arg);
            break;
        case AF_INET6:
            socklen = sizeof (struct sockaddr_in6);
            SOCKADDR_IN6 (&sock)->sin6_family = AF_INET6;
            SOCKADDR_IN6 (&sock)->sin6_port = htons (arg);
            if (addr == NULL) {
                SOCKADDR_IN6 (&sock)->sin6_addr = in6addr_any;
                addr = "any";
            } else {
                if (inet_pton (AF_INET6, addr, &(SOCKADDR_IN6 (&sock)->sin6_addr)) <= 0) {
                    ras_server_log (server, "unknown IPv6 address: %s", addr);
                    return -1;
                }
            }
            ras_server_log (server, "addr = %s, port = %d", addr, arg);
            break;
        default:
            return -1;
    }

    fd = socket (RAS_CONN (server)->domain, SOCK_STREAM, 0);
    if (fd < 0) {
        ras_server_log (server, "socket: %s", strerror (errno));
        goto fd_opened;
    }
    if (RAS_CONN (server)->domain == AF_INET6) {
        int yes = 1;
        setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof (yes));
    }
    if (RAS_CONN (server)->domain == AF_LOCAL) {
        fchmod (fd, arg);
    }

    if (bind (fd, SOCKADDR (&sock), socklen)) {
        ras_server_log (server, "bind: %s", strerror (errno));
        goto fd_opened;
    }

    if (listen (fd, SOMAXCONN)) {
        ras_server_log (server, "listen: %s", strerror (errno));
        goto fd_opened;
    }

    RAS_CONN (server)->fd = fd;
    RAS_CONN (server)->fd_is_set = true;
    server->addr = sock;
    server->addrlen = socklen;

    return 0;

fd_opened:
    close (fd);

    return -1;
}