210 lines
4.8 KiB
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;
|
|
}
|