summaryrefslogtreecommitdiff
path: root/src/tcp.c
blob: 726aed38a07bc1db0859e10a472105de3c5672f8 (plain)
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include "logging.h"
#include "tcp.h"


struct tcp_sock_t *tcp_open(uint32_t port)
{
	struct tcp_sock_t *this = calloc(1, sizeof *this);
	if (this == NULL) {
		ERR("callocing this failed");
		goto error;
	}

	// Open [S]ocket [D]escriptor
	this->sd = -1;
	this->sd = socket(AF_INET6, SOCK_STREAM, 0);
	if (this->sd < 0) {
		ERR("sockect open failed");
		goto error;
	}

	// Configure socket params
	struct sockaddr_in6 addr;
	memset(&addr, 0, sizeof addr);
	addr.sin6_family = AF_INET6;
	addr.sin6_port = htons(port);
	addr.sin6_addr = in6addr_any;

	// Bind to localhost
	if (bind(this->sd,
	        (struct sockaddr *)&addr,
	        sizeof addr) < 0) {
		ERR("Bind on port failed. "
		    "Requested port may be taken or require root permissions.");
		goto error;
	}

	// Let kernel over-accept max number of connections
	if (listen(this->sd, HTTP_MAX_PENDING_CONNS) < 0) {
		ERR("listen failed on socket");
		goto error;
	}

	return this;

error:
	if (this != NULL) {
		if (this->sd != -1) {
			close(this->sd);
		}
		free(this);
	}
	return NULL;
}

void tcp_close(struct tcp_sock_t *this)
{
	close(this->sd);
	free(this);
}

uint32_t tcp_port_number_get(struct tcp_sock_t *sock)
{
	sock->info_size = sizeof sock->info;
	int query_status = getsockname(
	                               sock->sd,
	                               (struct sockaddr *) &(sock->info),
	                               &(sock->info_size));
	if (query_status == -1) {
		ERR("query on socket port number failed");
		goto error;
	}

	return ntohs(sock->info.sin6_port);

error:
	return 0;
}

struct http_packet_t *tcp_packet_get(struct tcp_conn_t *tcp,
                                     struct http_message_t *msg)
{
	// Alloc packet ==---------------------------------------------------==
	struct http_packet_t *pkt = packet_new(msg);
	if (pkt == NULL) {
		ERR("failed to create packet for incoming tcp message");
		goto error;
	}

	size_t want_size = packet_pending_bytes(pkt);
	if (want_size == 0)
		return pkt;

	while (want_size != 0 && !msg->is_completed) {
		NOTE("TCP: Getting %d bytes", want_size);
		uint8_t *subbuffer = pkt->buffer + pkt->filled_size;
		ssize_t gotten_size = recv(tcp->sd, subbuffer, want_size, 0);
		if (gotten_size < 0) {
			int errno_saved = errno;
			ERR("recv failed with err %d:%s", errno_saved,
				strerror(errno_saved));
			goto error;
		}
		NOTE("TCP: Got %d bytes", gotten_size);
		if (gotten_size == 0) {
			tcp->is_closed = 1;
			if (pkt->filled_size == 0) {
				// Client closed TCP conn
				goto error;
			} else {
				break;
			}
		}

		packet_mark_received(pkt, gotten_size);
		want_size = packet_pending_bytes(pkt);
	}

	NOTE("TCP: Received %lu bytes", pkt->filled_size);
	return pkt;	
	 
error:
	if (pkt != NULL)
		packet_free(pkt);
	return NULL;
}

// TODO: handle EPIPE and SIGPIPE with MSG_NOSIGNAL and check for pipe closures
void tcp_packet_send(struct tcp_conn_t *conn, struct http_packet_t *pkt)
{
	ssize_t remaining = pkt->filled_size;
	ssize_t total = 0;
	while (remaining > 0) {
		ssize_t sent = send(conn->sd, pkt->buffer + total,
		                    remaining, 0);
		if (sent < 0) {
			ERR("Failed to sent data over TCP");
			exit(-1); // TODO: unify exits
		}

		total += sent;
		remaining -= sent;
	}
	NOTE("TCP: sent %lu bytes", total);
}


struct tcp_conn_t *tcp_conn_accept(struct tcp_sock_t *sock)
{
	struct tcp_conn_t *conn = calloc(1, sizeof *conn);
	if (conn == NULL) {
		ERR("Calloc for connection struct failed");
		goto error;
	}

	conn->sd = accept(sock->sd, NULL, NULL);
	if (conn->sd < 0) {
		ERR("accept failed");
		goto error;
	}

	return conn;

error:
	if (conn != NULL)
		free(conn);
	return NULL;
}

void tcp_conn_close(struct tcp_conn_t *conn)
{
	close(conn->sd);
	free(conn);
}