4 * This module handles client-side sockets opened by the Citadel server (for
5 * the client side of Internet protocols, etc.) It does _not_ handle client
6 * sockets for the Citadel client; for that you must look in ipc_c_tcp.c
7 * (which, uncoincidentally, bears a striking similarity to this file).
16 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
26 #include <libcitadel.h>
32 #include "sysdep_decls.h"
34 #include "clientsocket.h"
37 #define INADDR_NONE 0xffffffff
40 int sock_connect(char *host
, char *service
, char *protocol
)
45 struct sockaddr_in sin
;
46 struct sockaddr_in egress_sin
;
49 if ((host
== NULL
) || IsEmptyStr(host
))
51 if ((service
== NULL
) || IsEmptyStr(service
))
53 if ((protocol
== NULL
) || IsEmptyStr(protocol
))
56 memset(&sin
, 0, sizeof(sin
));
57 sin
.sin_family
= AF_INET
;
59 pse
= getservbyname(service
, protocol
);
61 sin
.sin_port
= pse
->s_port
;
62 } else if ((sin
.sin_port
= htons((u_short
) atoi(service
))) == 0) {
63 CtdlLogPrintf(CTDL_CRIT
, "Can't get %s service entry: %s\n",
64 service
, strerror(errno
));
67 phe
= gethostbyname(host
);
69 memcpy(&sin
.sin_addr
, phe
->h_addr
, phe
->h_length
);
70 } else if ((sin
.sin_addr
.s_addr
= inet_addr(host
)) == INADDR_NONE
) {
71 CtdlLogPrintf(CTDL_ERR
, "Can't get %s host entry: %s\n",
72 host
, strerror(errno
));
75 if ((ppe
= getprotobyname(protocol
)) == 0) {
76 CtdlLogPrintf(CTDL_CRIT
, "Can't get %s protocol entry: %s\n",
77 protocol
, strerror(errno
));
80 if (!strcmp(protocol
, "udp")) {
86 s
= socket(PF_INET
, type
, ppe
->p_proto
);
88 CtdlLogPrintf(CTDL_CRIT
, "Can't create socket: %s\n", strerror(errno
));
92 /* If citserver is bound to a specific IP address on the host, make
93 * sure we use that address for outbound connections.
95 memset(&egress_sin
, 0, sizeof(egress_sin
));
96 egress_sin
.sin_family
= AF_INET
;
97 if (!IsEmptyStr(config
.c_ip_addr
)) {
98 egress_sin
.sin_addr
.s_addr
= inet_addr(config
.c_ip_addr
);
99 if (egress_sin
.sin_addr
.s_addr
== !INADDR_ANY
) {
100 egress_sin
.sin_addr
.s_addr
= INADDR_ANY
;
103 /* If this bind fails, no problem; we can still use INADDR_ANY */
104 bind(s
, (struct sockaddr
*)&egress_sin
, sizeof(egress_sin
));
107 /* Now try to connect to the remote host. */
108 if (connect(s
, (struct sockaddr
*) &sin
, sizeof(sin
)) < 0) {
109 CtdlLogPrintf(CTDL_ERR
, "Can't connect to %s:%s: %s\n",
110 host
, service
, strerror(errno
));
121 * sock_read_to() - input binary data from socket, with a settable timeout.
122 * Returns the number of bytes read, or -1 for error.
123 * If keep_reading_until_full is nonzero, we keep reading until we get the number of requested bytes
125 int sock_read_to(int sock
, char *buf
, int bytes
, int timeout
, int keep_reading_until_full
)
139 retval
= select(sock
+1, &rfds
, NULL
, NULL
, &tv
);
141 if (FD_ISSET(sock
, &rfds
) == 0) { /* timed out */
142 CtdlLogPrintf(CTDL_ERR
, "sock_read() timed out.\n");
146 rlen
= read(sock
, &buf
[len
], bytes
-len
);
148 CtdlLogPrintf(CTDL_ERR
, "sock_read() failed: %s\n",
153 if (!keep_reading_until_full
) return(len
);
160 * sock_read() - input binary data from socket.
161 * Returns the number of bytes read, or -1 for error.
163 INLINE
int sock_read(int sock
, char *buf
, int bytes
, int keep_reading_until_full
)
165 return sock_read_to(sock
, buf
, bytes
, CLIENT_TIMEOUT
, keep_reading_until_full
);
170 * sock_write() - send binary to server.
171 * Returns the number of bytes written, or -1 for error.
173 int sock_write(int sock
, char *buf
, int nbytes
)
175 int bytes_written
= 0;
177 while (bytes_written
< nbytes
) {
178 retval
= write(sock
, &buf
[bytes_written
],
179 nbytes
- bytes_written
);
183 bytes_written
= bytes_written
+ retval
;
185 return (bytes_written
);
191 * Input string from socket - implemented in terms of sock_read()
194 int sock_getln(int sock
, char *buf
, int bufsize
)
198 /* Read one character at a time.
201 if (sock_read(sock
, &buf
[i
], 1, 1) < 0) return(-1);
202 if (buf
[i
] == '\n' || i
== (bufsize
-1))
206 /* If we got a long line, discard characters until the newline.
208 if (i
== (bufsize
-1))
209 while (buf
[i
] != '\n')
210 if (sock_read(sock
, &buf
[i
], 1, 1) < 0) return(-1);
212 /* Strip any trailing CR and LF characters.
216 && ( (buf
[i
- 1]==13)
217 || ( buf
[i
- 1]==10)) ) {
225 * Multiline version of sock_gets() ... this is a convenience function for
226 * client side protocol implementations. It only returns the first line of
227 * a multiline response, discarding the rest.
229 int ml_sock_gets(int sock
, char *buf
) {
233 g
= sock_getln(sock
, buf
, SIZ
);
234 if (g
< 4) return(g
);
235 if (buf
[3] != '-') return(g
);
238 g
= sock_getln(sock
, bigbuf
, SIZ
);
239 if (g
< 0) return(g
);
240 } while ( (g
>= 4) && (bigbuf
[3] == '-') );
247 * sock_puts() - send line to server - implemented in terms of serv_write()
248 * Returns the number of bytes written, or -1 for error.
250 int sock_puts(int sock
, char *buf
)
254 i
= sock_write(sock
, buf
, strlen(buf
));
256 j
= sock_write(sock
, "\n", 1);