Create repository
[fwd.git] / prod / qdsocket.c
1 /*
2  * QuanDocs socket API.
3  * (Convenience wrapper around BSD sockets)
4  *
5  * Copyright (C) 2004, 2015 by Chris Jaekl
6  */
7
8 #include <arpa/inet.h>
9 #include <errno.h>
10 #include <netdb.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/socket.h>
14 #include <sys/types.h>
15
16 #include "qdunittest.h"
17
18 #include "qderrhandler.h"
19 #include "qdsocket.h"
20 #include "qdtypes.h"
21
22 void qdSockCheckClose(struct qdSocket *sock)
23 {
24     if ((sock->closeRequested) && (0 == sock->toWrite)) {
25         // printf("\nCLOSE(%d)\n", sock->sd);
26         close(sock->sd);
27         memset(sock, 0x00, sizeof(struct qdSocket));
28     }
29 }
30
31
32
33 void qdSockClose(struct qdSocket *sock)
34 {
35     assert(NULL != sock);
36
37     if (0 == sock->sd) {
38         return;
39     }
40
41     sock->closeRequested = TRUE;
42     qdSockCheckClose(sock);
43 }
44
45 // Returns 0 on success, non-zero on failure
46 int qdSockConnect(struct qdSocket *sock, const char * host, int port)
47 {
48     int ret = 0;
49     struct hostent * he;
50     struct sockaddr_in sa;
51
52     assert(NULL != sock);
53     assert(0 == sock->sd);
54     assert(0 == sock->closeRequested);
55
56     memset(&sa, 0x00, sizeof(struct sockaddr_in));
57
58     he = gethostbyname(host);
59     if (NULL == he) {
60         qdAbort("gethostbyname() failed", h_errno);
61     }
62
63
64     sa.sin_family = AF_INET;
65     sa.sin_port = htons(port);
66     memcpy(&(sa.sin_addr), (he->h_addr_list[0]), sizeof(struct in_addr));
67
68     sock->sd = socket(AF_INET, SOCK_STREAM, 0);
69     if (sock->sd < 0) {
70         qdAbort("Could not create client socket.", sock->sd);
71     }
72
73     ret = connect(sock->sd, (struct sockaddr *)&sa, sizeof(sa));
74     if (ret < 0) {
75         return ret;     // Could not connect 
76     }
77 }
78
79
80 ssize_t qdSockFlush(struct qdSocket *to)
81 {
82     ssize_t written;
83     char * p;
84     char * q;
85     int idx;
86
87     written = write(to->sd, to->buf, to->toWrite);
88     if (written >= 0) {
89         assert(written <= to->toWrite);
90         to->toWrite -= written;
91
92         p = to->buf;
93         q = p + written;
94         for (idx = 0; idx < to->toWrite; ++idx) {
95             *p = *q;
96             p++;
97             q++;
98         }
99     }
100     else if (((-1) == written) && ((EAGAIN == errno) || (EWOULDBLOCK == errno))) {
101         // Socket is busy, leave pending write for later
102         written = 0;
103     }
104     else {
105         // Some unexpected error has occurred on the socket.
106         // Close it.
107         to->toWrite = 0;        // abandon unwritten data that we cannot flush
108         // TODO:  optionally log this condition, so that a sysadmin can see what's happening
109         qdSockClose(to);
110     }
111
112     return written;
113 }
114
115 void qdSockInit(struct qdSocket *sock)
116 {
117     memset(sock, 0x00, sizeof(struct qdSocket));
118 }
119
120 ssize_t qdSockWrite(struct qdSocket *to, char * buf, ssize_t n)
121 {
122     assert(NULL != to);
123     assert(NULL != buf);
124     ssize_t written;
125
126     if ((to->toWrite + n) > QD_SOCK_BUF_SIZE) {
127         // TODO:  implement this functionality
128         qdAbort("Unimplemented:  write buffer overflow.  Aborting.", 0);
129     }
130
131     // TODO:  should be smarter here (use a circular buffer to avoid O(n^2) worst-case performance)
132     memcpy((to->buf + to->toWrite), buf, n);
133     to->toWrite += n;
134
135     return qdSockFlush(to);
136 }
137
138