-
Notifications
You must be signed in to change notification settings - Fork 7
/
sock.c
239 lines (206 loc) · 5.73 KB
/
sock.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#ifdef _WIN32
// Need to say we are on recent enough windows to get getaddrinfo...
// http://stackoverflow.com/questions/12765743/getaddrinfo-on-win32
#define _WIN32_WINNT 0x0501
#include <winsock2.h>
#include <ws2tcpip.h>
#endif /* _WIN32 */
#if defined __linux__
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>
typedef int SOCKET;
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket(s) close(s);
#endif /* __linux__ */
// Thanks: http://beej.us/guide/bgnet/
// https://github.com/jimloco/Csocket
#define BUFFER_SIZE 1024*1024
struct handle {
SOCKET sock;
char wbuf[BUFFER_SIZE];
char rbuf[BUFFER_SIZE+1];
size_t roff; // Read pointer
size_t eoff; // Read end
};
#ifdef __cplusplus
extern "C" {
#endif
int sock_init() {
#ifdef _WIN32
// Init the windows sockets
WSADATA wsaData;
if( WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR) {
return -1;
}
#endif /* _WIN32 */
return 0;
}
void sock_shutdown() {
#ifdef _WIN32
WSACleanup();
#endif /* _WIN32 */
}
struct handle* init_struct(SOCKET sock) {
struct handle* h = (struct handle*) malloc(sizeof(struct handle));
if(h) {
h->sock = sock;
h->roff = 0;
h->eoff = 0;
h->rbuf[BUFFER_SIZE] = '\0'; // Overflow protection for long strings
}
return h;
}
void* tcp_sock_open(const char* name) {
// Extract hostname / port
char* string = strdup(name);
if(!string)
return NULL;
char* colon = strchr(string, ':');
if(!colon) {
free(string);
return NULL;
}
*colon = '\0'; // Split string into hostname and port
const char* hostname = string;
const char* port = colon + 1;
int status;
struct addrinfo hints, *res, *p;
// Setup hints - we want TCP and dont care if its IPv6 or IPv4
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
// Look up the host
if ((status = getaddrinfo(hostname, port, &hints, &res)) != 0) {
free(string);
return NULL;
}
free(string);
// Try and connect
SOCKET sock = INVALID_SOCKET;
for(p = res; sock == INVALID_SOCKET && p != NULL; p = p->ai_next) {
sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sock != INVALID_SOCKET) {
status = connect(sock, p->ai_addr, p->ai_addrlen);
if(status == SOCKET_ERROR) {
closesocket(sock);
sock = INVALID_SOCKET;
}
}
}
freeaddrinfo(res); // free the linked list
if( sock == INVALID_SOCKET ) {
return NULL;
}
// Create handle
return init_struct(sock);
}
#ifdef __linux__
void* unix_sock_open(const char* name) {
// Build endpoint details
struct sockaddr_un endpoint;
if(strlen(name) == 0 || strlen(name) > sizeof(endpoint.sun_path))
return NULL; // Invalid socket name
endpoint.sun_family = AF_UNIX;
strcpy(endpoint.sun_path, name);
size_t len = strlen(endpoint.sun_path) + sizeof(endpoint.sun_family);
if(endpoint.sun_path[0] == '@')
endpoint.sun_path[0] = '\0'; // Use @ for abstract namespace
// Create socket
SOCKET sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock == INVALID_SOCKET) {
return NULL;
}
// Connect
if( connect(sock, (struct sockaddr *)&endpoint, len) == SOCKET_ERROR) {
closesocket(sock);
return NULL;
}
// Create handle
return init_struct(sock);
}
#endif
void* sock_open(const char* uri) {
size_t len = strlen(uri);
if( len > 6 && strncmp("tcp://", uri, 6) == 0 ) {
return tcp_sock_open(uri+6);
}
#ifdef __linux__
else if( len > 7 && strncmp("unix://", uri, 7) == 0 ) {
return unix_sock_open(uri+7);
}
#endif
return NULL;
}
void sock_close(void* handle) {
if(!handle)
return;
struct handle* h = (struct handle*) handle;
closesocket(h->sock);
free(h);
}
int sock_writeln(void* handle, const char* data) {
// Validate input
if(!handle)
return 0; // Invalid handle
size_t len = strlen(data);
if(len >= BUFFER_SIZE)
return 0; // String too big
struct handle* h = (struct handle*) handle;
// Create output string (replace null termination with newline)
memcpy(h->wbuf, data, len);
h->wbuf[len] = '\n';
len++;
// Write
int ret = 0;
int done = 0;
while(ret != -1 && done != len) {
ret = send(h->sock, h->wbuf+done, len-done, 0);
done += ret;
}
return ret == -1 ? 0 : 1; // Success if ret != -1
}
const char* sock_readln(void* handle) {
// Validate input
if(!handle)
return 0;
struct handle* h = (struct handle*) handle;
// Prepare read - move down any spare data from last time
if( h->roff > h->eoff ) {
memmove(h->rbuf, h->rbuf + h->eoff, h->roff - h->eoff);
h->roff -= h->eoff;
} else {
h->roff = 0;
}
// Read
int ret = 0;
char* end = (char*) memchr((char*)(h->rbuf), '\n', h->roff);
while(ret != -1 && h->roff != BUFFER_SIZE && end == NULL) {
ret = recv(h->sock, h->rbuf + h->roff, BUFFER_SIZE - h->roff, 0);
if( ret != -1 )
end = (char*) memchr((char*)(h->rbuf + h->roff), '\n', ret); // Search for \n
h->roff += ret;
}
// Tidy up string
if( ret == -1 ) {
h->rbuf[0] = '\0'; // Empty string on error
}
if( end ) {
*end = '\0'; // Replace newline
h->eoff = end + 1 - h->rbuf; // Store where we got to
} else {
h->roff = 0;
}
return h->rbuf;
}
#ifdef __cplusplus
}
#endif