1
/* Copyright (C) 2008 PrimeBase Technologies GmbH, Germany
3
* PrimeBase Media Stream for MySQL
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
* Original author: Paul McCullagh (H&G2JCtL)
20
* Continued development: Barry Leslie
32
#include <sys/types.h>
36
typedef int socklen_t;
38
#define CLOSE_SOCKET(s) closesocket(s)
39
#define IOCTL_SOCKET ioctlsocket
40
#define SOCKET_ERRORNO WSAGetLastError()
41
#define EWOULDBLOCK WSAEWOULDBLOCK
44
#include <sys/ioctl.h>
45
#include <sys/socket.h>
47
#include <netinet/in.h>
48
#include <arpa/inet.h>
49
#include <netinet/in.h>
50
#include <netinet/tcp.h>
51
#include <sys/select.h>
54
extern void unix_close(int h);
55
#define CLOSE_SOCKET(s) unix_close(s)
56
#define IOCTL_SOCKET ioctl
57
#define SOCKET_ERRORNO errno
68
#include "CSStrUtil.h"
71
void CSSocket::initSockets()
76
WORD version = MAKEWORD (1,1);
77
static int inited = 0;
80
err = WSAStartup(version, &data);
83
CSException::throwException(CS_CONTEXT, err, "WSAStartup error");
93
* ---------------------------------------------------------------
94
* CORE SYSTEM SOCKET FACTORY
97
CSSocket *CSSocket::newSocket()
106
* ---------------------------------------------------------------
110
void CSSocket::formatAddress(size_t size, char *buffer)
113
cs_strcpy(size, buffer, iHost);
115
cs_strcat(size, buffer, ":");
120
cs_strcat(size, buffer, iService);
123
void CSSocket::throwError(const char *func, const char *file, int line, char *address, int err)
126
CSException::throwFileError(func, file, line, address, err);
128
CSException::throwEOFError(func, file, line, address);
131
void CSSocket::throwError(const char *func, const char *file, int line, int err)
133
char address[CS_SOCKET_ADDRESS_SIZE];
135
formatAddress(CS_SOCKET_ADDRESS_SIZE, address);
136
throwError(func, file, line, address, err);
139
void CSSocket::setNoDelay()
143
if (setsockopt(iHandle, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) == -1)
144
CSException::throwOSError(CS_CONTEXT, SOCKET_ERRORNO);
147
void CSSocket::setNonBlocking()
150
unsigned long block = 1;
152
if (IOCTL_SOCKET(iHandle, FIONBIO, &block) != 0)
153
throwError(CS_CONTEXT, SOCKET_ERRORNO);
157
void CSSocket::setBlocking()
159
/* No timeout, set blocking: */
161
unsigned long block = 0;
163
if (IOCTL_SOCKET(iHandle, FIONBIO, &block) != 0)
164
throwError(CS_CONTEXT, SOCKET_ERRORNO);
168
void CSSocket::openInternal()
170
iHandle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
172
CSException::throwOSError(CS_CONTEXT, SOCKET_ERRORNO);
177
void CSSocket::writeBlock(const void *data, size_t len)
183
out = send(iHandle, (const char *) data, len, 0);
186
int err = SOCKET_ERRORNO;
188
if (err == EWOULDBLOCK || err == EINTR)
190
throwError(CS_CONTEXT, err);
192
if ((size_t) out > len)
195
data = ((char *) data) + (size_t) out;
200
int CSSocket::timeoutRead(CSThread *self, void *buffer, size_t length)
204
uint64_t timeout = iTimeout * 1000;
206
start_time = CSTime::getTimeCurrentTicks();
209
in = recv(iHandle, (char *) buffer, length, 0);
211
if (SOCKET_ERRORNO == EWOULDBLOCK) {
214
struct timeval tv_timeout;
219
time_diff = CSTime::getTimeCurrentTicks() - start_time;
220
if (time_diff >= timeout) {
221
char address[CS_SOCKET_ADDRESS_SIZE];
223
formatAddress(CS_SOCKET_ADDRESS_SIZE, address);
224
CSException::throwExceptionf(CS_CONTEXT, CS_ERR_RECEIVE_TIMEOUT, "Receive timeout: %lu ms, on: %s", iTimeout, address);
227
/* Calculate how much time we can wait: */
228
time_diff = timeout - time_diff;
229
tv_timeout.tv_sec = (long)time_diff / 1000000;
230
tv_timeout.tv_usec = (long)time_diff % 1000000;
232
FD_SET(iHandle, &readfds);
233
in = select(iHandle+1, &readfds, NULL, NULL, &tv_timeout);
242
* ---------------------------------------------------------------
243
* SOCKET BASED ON THE STANDARD C SOCKET
246
void CSSocket::setTimeout(uint32_t milli_sec)
248
if (iTimeout != milli_sec) {
249
if ((iTimeout = milli_sec))
256
CSOutputStream *CSSocket::getOutputStream()
258
return CSSocketOutputStream::newStream(RETAIN(this));
261
CSInputStream *CSSocket::getInputStream()
263
return CSSocketInputStream::newStream(RETAIN(this));
266
void CSSocket::publish(char *service, int default_port)
271
struct servent *servp;
272
struct sockaddr_in server;
278
if (isdigit(service[0])) {
279
int i = atoi(service);
282
CSException::throwCoreError(CS_CONTEXT, CS_ERR_BAD_ADDRESS, service);
284
s.s_port = htons((uint16_t) i);
285
iService = cs_strdup(service);
287
else if ((servp = getservbyname(service, "tcp")) == NULL) {
289
CSException::throwCoreError(CS_CONTEXT, CS_ERR_UNKNOWN_SERVICE, service);
291
s.s_port = htons((uint16_t) default_port);
292
iService = cs_strdup(default_port);
295
iService = cs_strdup(service);
299
CSException::throwCoreError(CS_CONTEXT, CS_ERR_UNKNOWN_SERVICE, "");
301
s.s_port = htons((uint16_t) default_port);
302
iService = cs_strdup(default_port);
305
iPort = ntohs(servp->s_port);
307
memset(&server, 0, sizeof(server));
308
server.sin_family = AF_INET;
309
server.sin_addr.s_addr = INADDR_ANY;
310
server.sin_port = (uint16_t) servp->s_port;
312
if (setsockopt(iHandle, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(int)) == -1)
313
CSException::throwOSError(CS_CONTEXT, SOCKET_ERRORNO);
315
if (bind(iHandle, (struct sockaddr *) &server, sizeof(server)) == -1)
316
CSException::throwOSError(CS_CONTEXT, SOCKET_ERRORNO);
318
if (listen(iHandle, SOMAXCONN) == -1)
319
CSException::throwOSError(CS_CONTEXT, SOCKET_ERRORNO);
329
void CSSocket::open(CSSocket *listener)
336
char address[CS_SOCKET_ADDRESS_SIZE];
337
struct sockaddr_in remote;
338
socklen_t addrlen = sizeof(remote);
340
/* First get all the information we need from the listener: */
341
listener_handle = ((CSSocket *) listener)->iHandle;
342
listener->formatAddress(CS_SOCKET_ADDRESS_SIZE, address);
344
/* I want to make sure no error occurs after the connect!
345
* So I allocate a buffer for the host name up front.
346
* This means it may be to small, but this is not a problem
347
* because the host name stored here is is only used for display
348
* of error message etc.
350
iHost = (char *) cs_malloc(100);
351
iHandle = accept(listener_handle, (struct sockaddr *) &remote, &addrlen);
353
throwError(CS_CONTEXT, address, SOCKET_ERRORNO);
355
cs_strcpy(100, iHost, inet_ntoa(remote.sin_addr));
356
iPort = ntohs(remote.sin_port);
369
void CSSocket::open(char *address, int default_port)
374
char *portp = strchr(address, ':');
376
struct servent *servp;
377
struct hostent *hostp;
378
struct sockaddr_in server;
382
iHost = cs_strdup(address);
384
CSException::throwCoreError(CS_CONTEXT, CS_ERR_BAD_ADDRESS, address);
385
iService = cs_strdup(default_port);
388
iHost = cs_strdup(address, (size_t) (portp - address));
389
iService = cs_strdup(portp+1);
392
if (isdigit(iService[0])) {
393
int i = atoi(iService);
396
CSException::throwCoreError(CS_CONTEXT, CS_ERR_BAD_ADDRESS, address);
398
s.s_port = htons((uint16_t) i);
400
else if ((servp = getservbyname(iService, "tcp")) == NULL)
401
CSException::throwCoreError(CS_CONTEXT, CS_ERR_UNKNOWN_SERVICE, iService);
402
iPort = (int) ntohs(servp->s_port);
404
if ((hostp = gethostbyname(iHost)) == 0)
405
CSException::throwCoreError(CS_CONTEXT, CS_ERR_UNKNOWN_HOST, iHost);
407
memset(&server, 0, sizeof(server));
408
server.sin_family = AF_INET;
409
memcpy(&server.sin_addr, hostp->h_addr, (size_t) hostp->h_length);
410
server.sin_port = (uint16_t) servp->s_port;
411
if (connect(iHandle, (struct sockaddr *) &server, sizeof(server)) == -1)
412
throwError(CS_CONTEXT, SOCKET_ERRORNO);
422
void CSSocket::close()
426
shutdown(iHandle, SHUT_RDWR);
427
/* shutdown does not close the socket!!? */
428
CLOSE_SOCKET(iHandle);
446
size_t CSSocket::read(void *data, size_t len)
451
/* recv, by default will block until at lease one byte is
453
* So a return of zero means EOF!
457
in = timeoutRead(self, data, len);
459
in = recv(iHandle, (char *) data, len, 0);
462
/* Note, we actually ignore all errors on the socket.
463
* If no data was returned by the read so far, then
464
* the error will be considered EOF.
466
int err = SOCKET_ERRORNO;
468
if (err == EWOULDBLOCK || err == EINTR)
470
throwError(CS_CONTEXT, err);
473
return_((size_t) in);
482
if (read(buffer, 1) == 1)
494
void CSSocket::write(const void *data, size_t len)
496
#ifdef CS_USE_OUTPUT_BUFFER
497
if (len <= CS_MIN_WRITE_SIZE) {
498
if (iDataLen + len > CS_OUTPUT_BUFFER_SIZE) {
499
/* This is the amount of data that will still fit
502
size_t tfer = CS_OUTPUT_BUFFER_SIZE - iDataLen;
504
memcpy(iOutputBuffer + iDataLen, data, tfer);
507
memcpy(iOutputBuffer, ((char *) data) + tfer, len);
511
memcpy(iOutputBuffer + iDataLen, data, len);
516
/* If the block give is large enough, the
517
* writing directly from the block saves copying the
518
* data to the local output buffer buffer:
521
writeBlock(data, len);
524
writeBlock(data, len);
528
void CSSocket::write(char ch)
535
void CSSocket::flush()
537
#ifdef CS_USE_OUTPUT_BUFFER
540
if ((len = iDataLen)) {
542
/* Note: we discard the data to be written if an
545
writeBlock(iOutputBuffer, len);
550
const char *CSSocket::identify()
556
formatAddress(200, buffer);
557
iIdentity = cs_strdup(buffer);