#define _GNU_SOURCE #include #include #include #include #include #include #include #include #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; }