This repository has been archived on 2020-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
eilduscd/src/ildus-connection.c

210 lines
4.8 KiB
C

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include "ildus-connection.h"
#include "common.h"
ildus_connection_t *
new_ildus_connection(char *name, int port, ildus_connection_error_t *error) {
ildus_connection_t *result = NULL;
int sockfd = -1;
int ret = -1;
struct addrinfo req, *ans, *tmpaddr;
char hname[NI_MAXHOST], portname[NI_MAXSERV];
memset(&req, 0, sizeof(req));
req.ai_flags = 0;
req.ai_family = AF_UNSPEC;
req.ai_socktype = SOCK_STREAM;
req.ai_protocol = IPPROTO_TCP;
if (getaddrinfo(name, NULL, &req, &ans) != 0) {
*error = ILDUS_CONNECTION_UNRESOLVED_HOST_ERROR;
return NULL;
}
tmpaddr = ans;
while (tmpaddr != NULL) {
((struct sockaddr_in *) tmpaddr->ai_addr)->sin_port = htons(port);
getnameinfo(tmpaddr->ai_addr, tmpaddr->ai_addrlen,
hname, sizeof(hname), portname, sizeof(portname),
NI_NUMERICHOST | NI_NUMERICSERV);
printf("Trying: %s %s\n", hname, portname);
sockfd =
socket(tmpaddr->ai_family, tmpaddr->ai_socktype, tmpaddr->ai_protocol);
if (sockfd < 0) {
printf("socket failed: %s", strerror(errno));
} else if ((ret = connect(sockfd, tmpaddr->ai_addr, tmpaddr->ai_addrlen))
< 0) {
printf( "connect failed: %s", strerror(errno));
close(sockfd);
} else {
break;
}
tmpaddr = tmpaddr->ai_next;
}
if (ret != 0 || sockfd < 0) {
freeaddrinfo(ans);
*error = ILDUS_CONNECTION_FAILED;
return NULL;
}
result = malloc(sizeof(ildus_connection_t));
result->fd = sockfd;
result->size = 1024;
result->buffer = malloc(result->size);
result->fill = 0;
result->seen_cr = FALSE;
freeaddrinfo(ans);
return result;
}
void del_ildus_connection(ildus_connection_t *c) {
if (c == NULL)
return;
if (c->fd >= 0) {
close(c->fd);
}
free(c);
}
static ildus_message_t *
new_ildus_message(void) {
ildus_message_t *result;
result = malloc(sizeof(ildus_message_t));
result->msg = NULL;
result->head_msg = NULL;
result->data = NULL;
return result;
}
void
del_ildus_message(ildus_message_t *m) {
int i;
if (m == NULL)
return;
free(m->msg);
free(m->head_msg);
for (i = 0 ; m->data != NULL && m->data[i] != NULL ; i++) {
printf("HERE\n");
free(m->data[i]);
}
free(m);
}
static int
ildus_get_more_data(ildus_connection_t *c, ildus_connection_error_t *error) {
int ret;
if (c->fill == c->size) {
*error = ILDUS_CONNECTION_CONNECTION_BUFFER_OVERFLOW;
return FALSE;
}
ret = read(c->fd, c->buffer + c->fill, c->size - c->fill);
if (ret <= 0) {
*error = ILDUS_CONNECTION_CONNECTION_READ_ERROR;
c->fd = -1;
return FALSE;
}
c->fill += ret;
return TRUE;
}
static char *
ildus_get_line(ildus_connection_t *c, ildus_connection_error_t *error) {
char *result = NULL;
int i = 0;
int start = -1;
i = start = (c->seen_cr ? 1 : 0);
for (;;) {
for (; i < c->fill; i++ ){
if (c->buffer[i] == '\r' || c->buffer[i] == '\n') {
/* End of a line detected */
c->seen_cr = (c->buffer[i] == '\r');
/* Note: \r or \n isn't dupped */
result = strndup(c->buffer + start, i - start);
/* Change i into the number of characters including \r or \n */
i++;
c->fill -= i;
memmove(c->buffer, c->buffer + i, c->fill);
return result;
}
}
if (!ildus_get_more_data(c, error)) {
return NULL;
}
}
}
ildus_message_t *
ildus_connection_get_message(ildus_connection_t *c,
ildus_connection_error_t *error) {
char *line = NULL;
ildus_message_t *result = NULL;
line = ildus_get_line(c, error);
if (!line) {
goto no_line;
}
if (strlen(line) < 4) {
free(line);
goto malformed;
}
result = new_ildus_message();
result->code = atoi(line);
result->long_msg = (line[3] == '-');
if (result->long_msg) {
int ndata = 0;
result->head_msg = line;
for (;;) {
line = ildus_get_line(c, error);
if (!line) {
goto no_line;
}
if (strlen(line) >= 4 && line[3] == ' ') {
break;
}
/* append to data */
result->data = realloc(result->data, (ndata + 2) * sizeof(char *));
result->data[ndata] = line;
result->data[ndata + 1] = NULL;
ndata++;
}
}
result->msg = line;
return result;
malformed:
*error = ILDUS_CONNECTION_MALFORMED_REPLY;
no_line:
del_ildus_message(result);
return NULL;
}
int
ildus_connection_send(ildus_connection_t *c, char *data) {
int len = strlen(data);
int written = 0;
while (written < len) {
int ret;
ret = write(c->fd, data + written, len - written);
if (ret < 0) {
return FALSE;
}
written += ret;
}
return TRUE;
}