-
Notifications
You must be signed in to change notification settings - Fork 2
/
net.c
166 lines (157 loc) · 4.99 KB
/
net.c
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "dsp.h"
#define LISTEN_BACKLOG 128
/// Static functions
static dsp_error parse_address (char *address, struct addrinfo **res)
{
char *colon = strrchr(address, ':');
if (!colon) {
return error(DSP_E_NETWORK, "Invalid network address, needs to be of "
"the form <host>:<port>");
}
int i = colon - address;
char *host = malloc((i + 1) * sizeof(char));
memcpy(host, address, i);
host[i] = '\0';
char *port = address + i + 1;
struct addrinfo hints = {0};
hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(host, port, &hints, res);
if (ret) {
*res = NULL;
free(host);
char const *err_msg = gai_strerror(ret);
return error(DSP_E_NETWORK, err_msg);
}
free(host);
return NULL;
}
static void *client (void *arg)
{
struct connection *conn = arg;
int ret = pthread_mutex_lock(&conn->mutex);
if (ret) return sys_error(DSP_E_SYSTEM, ret, NULL);
while (1) {
// The client blocks until it is signalled that there is a request in
// its buffer
while (!conn->buffer) {
if (ret = pthread_cond_wait(&conn->cond, &conn->mutex))
return sys_error(DSP_E_SYSTEM, ret, NULL);
}
// Send request
dsp_error err = send_request(conn);
if (err) {
if (ret = pthread_mutex_unlock(&conn->mutex))
return sys_error(DSP_E_SYSTEM, ret, NULL);
return err;
}
}
return NULL;
}
static dsp_error handle (struct dsp *dsp, int client, struct sockaddr_in *client_address)
{
return NULL;
}
/// Extern functions
//TODO: allow ipv6
dsp_error net_listen (struct dsp *dsp)
{
int listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener == -1) return sys_error(DSP_E_SYSTEM, errno,
"Failed to open listener network socket");
// Port in network order
uint16_t port = htons(dsp->tcp_port);
struct sockaddr_in address = {AF_INET, port, INADDR_ANY};
if (bind(listener, (struct sockaddr *) &address,
sizeof(struct sockaddr_in)))
return sys_error(DSP_E_SYSTEM, errno, "Failed to bind listener port");
if (listen(listener, LISTEN_BACKLOG))
return sys_error(DSP_E_SYSTEM, errno, NULL);
int client;
struct sockaddr_in client_address;
while (client = accept(listener, (struct sockaddr *) &client_address,
(socklen_t *) sizeof(struct sockaddr_in))) {
if (client < 0) {
switch (errno) {
case ENETDOWN:
case EPROTO:
case ENOPROTOOPT:
case EHOSTDOWN:
case ENONET:
case EHOSTUNREACH:
case EOPNOTSUPP:
case ENETUNREACH:
continue;
}
return sys_error(DSP_E_SYSTEM, errno,
"Failed to accept connection");
}
dsp_error err;
if (err = handle(dsp, client, &client_address)) return err;
}
return NULL;
}
//TODO: NAT hole-punching
dsp_error net_connect (char *address, struct connection **connection)
{
if (!(*connection = calloc(1, sizeof(struct connection)))) {
return sys_error(DSP_E_SYSTEM, errno, "Failed to allocate connection object");
}
(*connection)->address = address;
struct addrinfo *res, *rp;
// Perform an address lookup
dsp_error err = parse_address(address, &res);
if (err) {
free(*connection);
*connection = NULL;
return err;
}
// Run through address list until connection succeeds
for (rp = res; rp; rp = rp->ai_next) {
(*connection)->socket = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if ((*connection)->socket == -1) continue;
if (!connect((*connection)->socket, rp->ai_addr, rp->ai_addrlen))
break;
if (close((*connection)->socket)) {
freeaddrinfo(res);
free(*connection);
*connection = NULL;
return sys_error(DSP_E_SYSTEM, errno,
"Failed to close connection socket");
}
}
if (!rp) {
freeaddrinfo(res);
free(*connection);
*connection = NULL;
char *msg = NULL;
sprintf(msg, "Connection to <%s> failed", address);
return error(DSP_E_NETWORK, msg);
}
freeaddrinfo(res);
if (err = handshake(*connection)) return err;
// Initialize client thread
int ret = pthread_create(&(*connection)->thread, NULL, client,
*connection);
if (ret) {
free(*connection);
*connection = NULL;
return sys_error(DSP_E_SYSTEM, ret, "Failed to create connection thread");
}
return NULL;
}
dsp_error net_disconnect (struct connection *connection)
{
return NULL;
}