X-Git-Url: http://git.cascardo.info/?p=cascardo%2Flibreceita.git;a=blobdiff_plain;f=rnetclient.c;h=a9c75cd4702a5d1a1d281b73da274afc285798d7;hp=0a8ec87a1123eb13a8d564ca465d7c3e567a7707;hb=90cc697dec1fe387e27b28424d85fbc7df36e27f;hpb=990f2ded96a2ba150938b616e69f60ac4d8078f0 diff --git a/rnetclient.c b/rnetclient.c index 0a8ec87..a9c75cd 100644 --- a/rnetclient.c +++ b/rnetclient.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Thadeu Lima de Souza Cascardo + * Copyright (C) 2012-2013 Thadeu Lima de Souza Cascardo * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,8 +24,12 @@ #include #include #include +#include #include #include +#include "decfile.h" +#include "rnet_message.h" +#include "rnet_encode.h" static void * get_creds(char *certfile) { @@ -70,10 +74,10 @@ static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen) free(*out); return -1; } - *olen = zstrm.avail_out + 6; + *olen = zstrm.total_out + 6; (*out)[0] = 0x1; - (*out)[1] = (zstrm.avail_out >> 8); - (*out)[2] = (zstrm.avail_out & 0xff); + (*out)[1] = (zstrm.total_out >> 8); + (*out)[2] = (zstrm.total_out & 0xff); (*out)[3] = (len >> 8); (*out)[4] = (len & 0xff); (*out)[5] = 0x1; @@ -90,7 +94,7 @@ static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen) zstrm.opaque = Z_NULL; if ((r = inflateInit(&zstrm)) != Z_OK) return -1; - *olen = (buffer[3] << 8 & buffer[4]); + *olen = (buffer[3] << 8 | buffer[4]); *out = malloc(*olen); if (!out) { inflateEnd(&zstrm); @@ -111,49 +115,270 @@ static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen) return 0; } -int main(int argc, char **argv) +#define RNET_ADDRESS "receitanet.receita.fazenda.gov.br" + +static int connect_rnet(int *c) { + struct addrinfo *addresses; + struct addrinfo *addr; + struct addrinfo hint; struct sockaddr_in saddr; - int c; int r; - char buffer[1024]; + int fd = *c = -1; + int i; + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_UNSPEC; + hint.ai_socktype = SOCK_STREAM; + hint.ai_protocol = IPPROTO_TCP; + hint.ai_flags = AI_ADDRCONFIG; + r = getaddrinfo(RNET_ADDRESS, "3456", &hint, &addresses); + if (r) { + return r; + } + for (addr = addresses; addr != NULL; addr = addr->ai_next) { + fd = socket(addr->ai_family, addr->ai_socktype, + addr->ai_protocol); + if (fd >= 0) + if (!(r = connect(fd, addr->ai_addr, + addr->ai_addrlen))) + break; + close(fd); + fd = -1; + } + freeaddrinfo(addresses); + *c = fd; + if (fd == -1) + return EAI_SYSTEM; + return 0; +} + +static int handshake(int c) +{ + char buffer[16]; + int r; + buffer[0] = 1; + write(c, buffer, 1); + write(c, "00000000000000", 14); + r = read(c, buffer, 1); + if (r != 1 && buffer[0] != 'E') + return -1; + r = read(c, buffer, 14); + if (r != 14) + return -1; + return 0; +} + +static void usage(void) +{ + fprintf(stderr, "rnetclient [filename]\n"); + exit(1); +} + +static int rnet_send(gnutls_session_t session, char *buffer, size_t len) +{ + char *out; + size_t olen; + deflateRecord(buffer, len, &out, &olen); + gnutls_record_send(session, out, olen); + free(out); + return 0; +} + +static int rnet_recv(gnutls_session_t session, struct rnet_message **message) +{ char *out; size_t olen; + int r; + char *buffer; + size_t len; + rnet_message_expand(message, 6); + buffer = (*message)->buffer; + r = gnutls_record_recv(session, buffer, 6); + if (buffer[0] == 0x01) { + len = (buffer[1] << 8 | buffer[2]); + rnet_message_expand(message, len); + buffer = (*message)->buffer + 6; + r = gnutls_record_recv(session, buffer, len); + inflateRecord(buffer - 6, len + 6, &out, &olen); + rnet_message_del(*message); + *message = NULL; + rnet_message_expand(message, olen); + memcpy((*message)->buffer, out, olen); + (*message)->len = olen; + free(out); + } else { + len = (buffer[1] << 8 | buffer[2]); + rnet_message_expand(message, len - 1); + buffer = (*message)->buffer + 6; + r = gnutls_record_recv(session, buffer, len - 1); + (*message)->len = len + 4; + rnet_message_strip(*message, 4); + } + return 0; +} + +static void save_rec_file(char *cpf, char *buffer, int len) +{ + int fd; + char *filename; + char *home, *tmpdir; + mode_t mask; + size_t fnlen; + int r; + home = getenv("HOME"); + if (!home) { + tmpdir = getenv("TMPDIR"); + if (!tmpdir) + tmpdir = "/tmp"; + home = tmpdir; + } + fnlen = strlen(home) + strlen(cpf) + 13; + filename = malloc(fnlen); + snprintf(filename, fnlen, "%s/%s.REC.XXXXXX", home, cpf); + mask = umask(0177); + fd = mkstemp(filename); + if (fd < 0) { + fprintf(stderr, "Could not create receipt file: %s\n", + strerror(errno)); + goto out; + } + r = write(fd, buffer, len); + if (r != len) { + fprintf(stderr, "Could not write to receipt file%s%s\n", + r < 0 ? ": " : ".", + r < 0 ? strerror(errno) : ""); + goto out; + } + fprintf(stderr, "Wrote the receipt to %s.\n", filename); +out: + close(fd); + free(filename); + umask(mask); +} + +static void handle_response_text_and_file(char *cpf, struct rnet_message *message) +{ + char *value; + int vlen; + if (!rnet_message_parse(message, "texto", &value, &vlen)) + fprintf(stderr, "%.*s\n", vlen, value); + if (!rnet_message_parse(message, "arquivo", &value, &vlen)) + save_rec_file(cpf, value, vlen); +} + +static void handle_response_already_found(char *cpf, struct rnet_message *message) +{ + handle_response_text_and_file(cpf, message); +} + +static void handle_response_error(struct rnet_message *message) +{ + char *value; + int vlen; + if (!rnet_message_parse(message, "texto", &value, &vlen)) + fprintf(stderr, "%.*s\n", vlen, value); + fprintf(stderr, "Error transmiting DEC file.\n"); +} + +int main(int argc, char **argv) +{ + int c; + int r; + struct rnet_decfile *decfile; + struct rnet_message *message = NULL; gnutls_session_t session; + int finish = 0; + char *cpf; + + if (argc < 2) { + usage(); + } + + decfile = rnet_decfile_open(argv[1]); + if (!decfile) { + fprintf(stderr, "could not parse %s: %s\n", argv[1], strerror(errno)); + exit(1); + } + + cpf = rnet_decfile_get_header_field(decfile, "cpf"); + gnutls_global_init(); + session_new(&session); - c = socket(PF_INET, SOCK_STREAM, 0); - saddr.sin_family = AF_INET; - saddr.sin_port = htons(3456); - saddr.sin_addr.s_addr = inet_addr("161.148.185.11"); - r = connect(c, (struct sockaddr *) &saddr, sizeof(saddr)); - if (r < 0) { + r = connect_rnet(&c); + if (r) { fprintf(stderr, "error connecting to server: %s\n", - strerror(errno)); + r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); exit(1); } gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) c); - buffer[0] = 1; - write(c, buffer, 1); - write(c, "00000000000000", 14); - r = read(c, buffer, 1); - if (r != 1 && buffer[0] != 'E') - exit(1); - r = read(c, buffer, 14); - if (r != 14) + r = handshake(c); + if (r < 0) { exit(1); + } if ((r = gnutls_handshake(session)) < 0) fprintf(stderr, "error in handshake: %s\n", gnutls_strerror(r)); - else - fprintf(stderr, "handshake ok\n"); - r = read(0, buffer, sizeof(buffer)); - deflateRecord(buffer, r, &out, &olen); - gnutls_record_send(session, out, olen); - free(out); - while ((r = gnutls_record_recv(session, buffer, sizeof(buffer))) > 0) - write(1, buffer, r); + + rnet_encode(decfile, &message); + rnet_send(session, message->buffer, message->len); + rnet_message_del(message); + + message = NULL; + r = rnet_recv(session, &message); + if (r || !message || message->len == 0) { + fprintf(stderr, "error when receiving response\n"); + goto out; + } + switch (message->buffer[0]) { + case 1: /* go ahead */ + handle_response_text_and_file(cpf, message); + break; + case 3: /* error */ + handle_response_error(message); + finish = 1; + break; + case 4: + handle_response_already_found(cpf, message); + finish = 1; + break; + case 2: + case 5: + handle_response_text_and_file(cpf, message); + finish = 1; + break; + } + rnet_message_del(message); + + if (finish) + goto out; + + message = rnet_decfile_get_file(decfile); + rnet_send(session, message->buffer, message->len); + + message = NULL; + r = rnet_recv(session, &message); + if (r || !message || message->len == 0) { + fprintf(stderr, "error when receiving response\n"); + goto out; + } + switch (message->buffer[0]) { + case 3: /* error */ + handle_response_error(message); + break; + case 2: + case 4: + case 5: + case 1: + handle_response_text_and_file(cpf, message); + break; + } + +out: + gnutls_bye(session, GNUTLS_SHUT_RDWR); close(c); + rnet_decfile_close(decfile); gnutls_global_deinit(); + return 0; }