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 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
|
/** \file shared/sockets.c
* Socket functions available to server and clients.
*/
/*-
* This file is part of LCDproc.
*
* Feel free to use this in your own clients... :)
*/
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <fcntl.h>
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "report.h"
#include "sockets.h"
// Length of longest transmission allowed at once...
#define MAXMSG 8192
typedef struct sockaddr_in sockaddr_in;
/**
* Tries to resolve a resolve a hostname.
* \param name Pointer to resolves IP-address
* \param hostname Hostname or IP-address (as string)
* \param port Port number
* \return 0 on success, -1 on error.
*/
static int
sock_init_sockaddr (sockaddr_in *name, const char *hostname, unsigned short int port)
{
struct hostent *hostinfo;
memset (name, '\0', sizeof (*name));
name->sin_family = AF_INET;
name->sin_port = htons (port);
hostinfo = gethostbyname (hostname);
if (hostinfo == NULL) {
report (RPT_ERR, "sock_init_sockaddr: Unknown host %s.", hostname);
return -1;
}
name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
return 0;
}
/**
* Connect to server.
* \param host Hostname or IP-address
* \param port Port number
* \return socket file descriptor on success, -1 on error
*/
int
sock_connect (char *host, unsigned short int port)
{
struct sockaddr_in servername;
int sock;
int err = 0;
report (RPT_DEBUG, "sock_connect: Creating socket");
sock = socket (PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
report (RPT_ERR, "sock_connect: Error creating socket");
return sock;
}
debug (RPT_DEBUG, "sock_connect: Created socket (%i)", sock);
if (sock_init_sockaddr (&servername, host, port) < 0)
return -1;
err = connect (sock, (struct sockaddr *) &servername, sizeof (servername));
if (err < 0) {
report (RPT_ERR, "sock_connect: connect failed");
shutdown (sock, SHUT_RDWR);
return -1;
}
fcntl (sock, F_SETFL, O_NONBLOCK);
return sock;
}
/**
* Disconnect from server.
* \param fd Socket file descriptor
* \return 0 on success, -1 on error.
*/
int
sock_close (int fd)
{
int err;
err = shutdown (fd, SHUT_RDWR);
if (!err)
close (fd);
return err;
}
/**
* Send printf-like formatted output.
* \param fd Socket file descriptor
* \param format Format string
* \param ... Arguments to the format string
* \return Number of bytes sent.
*/
int
sock_printf(int fd, const char *format, .../*args*/ )
{
char buf[MAXMSG];
va_list ap;
int size = 0;
va_start(ap, format);
size = vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
if (size < 0) {
report(RPT_ERR, "sock_printf: vsnprintf failed");
return -1;
}
if (size > sizeof(buf))
report(RPT_WARNING, "sock_printf: vsnprintf truncated message");
return sock_send_string(fd, buf);
}
/**
* Send lines of text.
* \param fd Socket file descriptor
* \param string Pointer to the string to send.
* \return Number of bytes sent.
*/
int
sock_send_string (int fd, char *string)
{
return sock_send(fd, string, strlen(string));
}
/**
* Receive a line of text.
* Recv gives only one line per call...
* \param fd Socket file descriptor
* \param dest Pointer to buffer to store the received data
* \param maxlen Number of bytes to read at most (size of buffer)
* \return Number of bytes received.
*/
int
sock_recv_string (int fd, char *dest, size_t maxlen)
{
char *ptr = dest;
int recvBytes = 0;
if (!dest)
return -1;
if (maxlen <= 0)
return 0;
while (1) {
int err = read (fd, ptr, 1);
if (err == -1) {
if (errno == EAGAIN) {
if (recvBytes) {
// We've begun to read a string, but no bytes are
// available. Loop.
continue;
}
return 0;
} else {
report (RPT_ERR, "sock_recv_string: socket read error");
return err;
}
} else if (err == 0) {
return recvBytes;
}
recvBytes++;
// stop at max. bytes allowed, at NUL or at LF
if (recvBytes == maxlen || *ptr == '\0' || *ptr == '\n') {
*ptr = '\0';
break;
}
ptr++;
}
// Don't return an empty string
if (recvBytes == 1 && dest[0] == '\0')
return 0;
if (recvBytes < maxlen - 1)
dest[recvBytes] = '\0';
return recvBytes;
}
/**
* Send raw data.
* \param fd Socket file descriptor
* \param src Buffer holding the data to send
* \param size Number of bytes to send at most
* \return Number of bytes sent.
*/
int
sock_send (int fd, void *src, size_t size)
{
int offset = 0;
if (!src)
return -1;
while (offset != size) {
// write isn't guaranteed to send the entire string at once,
// so we have to sent it in a loop like this
int sent = write (fd, ((char *) src) + offset, size - offset);
if (sent == -1) {
if (errno != EAGAIN) {
report (RPT_ERR, "sock_send: socket write error");
report (RPT_DEBUG, "Message was: '%.*s'", size-offset, (char *) src);
return sent;
}
continue;
} else if (sent == 0) {
// when this returns zero, it generally means
// we got disconnected
return sent + offset;
}
offset += sent;
}
return offset;
}
/**
* Receive raw data.
* \param fd Socket file descriptor
* \param dest Pointer to buffer to store the received data
* \param maxlen Number of bytes to read at most (size of buffer)
* \return Number of bytes received.
*/
int
sock_recv (int fd, void *dest, size_t maxlen)
{
int err;
if (!dest)
return -1;
if (maxlen <= 0)
return 0;
err = read (fd, dest, maxlen);
if (err < 0) {
//report (RPT_DEBUG,"sock_recv: socket read error");
return err;
}
//debug(RPT_DEBUG, "sock_recv: Got message \"%s\"", (char *)dest);
return err;
}
/*****************************************************************************/
/**
* Return the error message for the last error occured.
* \return Error message string
*/
char*
sock_geterror(void)
{
return strerror(errno);
}
/**
* Send an already formatted error message to the client.
* \param fd socket
* \param message the message to send (without the "huh? ") */
int sock_send_error(int fd, char* message)
{
// simple: performance penalty isn't worth more work...
return sock_printf_error(fd, "%s", message);
}
/**
* Print printf-like formatted output to logfile and sends it to the client.
* \note don't add a the "huh? " to the message. This is done by this
* method
* \param fd socket
* \param format a printf format */
int
sock_printf_error(int fd, const char *format, .../*args*/ )
{
static const char huh[] = "huh? ";
char buf[MAXMSG];
va_list ap;
int size = 0;
strncpy(buf, huh, sizeof(huh)); // note: sizeof(huh) < MAXMSG
va_start(ap, format);
size = vsnprintf(buf + (sizeof(huh)-1), sizeof(buf) - (sizeof(huh)-1), format, ap);
buf[sizeof(buf)-1] = '\0';
va_end(ap);
if (size < 0) {
report(RPT_ERR, "sock_printf_error: vsnprintf failed");
return -1;
}
if (size >= sizeof(buf) - (sizeof(huh)-1))
report(RPT_WARNING, "sock_printf_error: vsnprintf truncated message");
report(RPT_INFO, "client error: %s", buf);
return sock_send_string(fd, buf);
}
|