2 * Copyright (C) 2012-2013 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
28 #include <gnutls/gnutls.h>
31 #include "rnet_message.h"
32 #include "rnet_encode.h"
34 static size_t chars2len (unsigned char buf[2]) {
35 return (buf[0] << 8 | buf[1]);
38 static void * get_creds(char *certfile)
40 static gnutls_certificate_credentials_t cred;
41 gnutls_certificate_allocate_credentials(&cred);
42 gnutls_certificate_set_x509_trust_file(cred, certfile,
47 static void session_new(gnutls_session_t *session)
50 cred = get_creds("cert.pem");
51 gnutls_init(session, GNUTLS_CLIENT);
52 gnutls_set_default_priority(*session);
53 gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, cred);
56 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen, int header)
60 zstrm.zalloc = Z_NULL;
62 zstrm.opaque = Z_NULL;
63 if ((r = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION)) != Z_OK)
65 *out = malloc(len * 2 + 36);
70 zstrm.next_in = buffer;
72 zstrm.next_out = *out + 6;
73 zstrm.avail_out = len * 2 + 30;
74 while ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
76 if ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
81 *olen = zstrm.total_out + 6;
83 (*out)[1] = (zstrm.total_out >> 8);
84 (*out)[2] = (zstrm.total_out & 0xff);
85 (*out)[3] = (len >> 8);
86 (*out)[4] = (len & 0xff);
87 (*out)[5] = header ? 0x01 : 0x0;
92 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
96 zstrm.zalloc = Z_NULL;
98 zstrm.opaque = Z_NULL;
99 if ((r = inflateInit(&zstrm)) != Z_OK)
101 *olen = chars2len(buffer+3);
102 *out = malloc(*olen);
107 zstrm.next_in = buffer + 6;
108 zstrm.avail_in = len - 6;
109 zstrm.next_out = *out;
110 zstrm.avail_out = *olen;
111 while ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
112 zstrm.avail_out > 0);
113 if ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
122 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
124 static int connect_rnet(int *c)
126 struct addrinfo *addresses;
127 struct addrinfo *addr;
128 struct addrinfo hint;
129 struct sockaddr_in saddr;
133 memset(&hint, 0, sizeof(hint));
134 hint.ai_family = AF_UNSPEC;
135 hint.ai_socktype = SOCK_STREAM;
136 hint.ai_protocol = IPPROTO_TCP;
137 hint.ai_flags = AI_ADDRCONFIG;
138 r = getaddrinfo(RNET_ADDRESS, "3456", &hint, &addresses);
142 for (addr = addresses; addr != NULL; addr = addr->ai_next) {
143 fd = socket(addr->ai_family, addr->ai_socktype,
146 if (!(r = connect(fd, addr->ai_addr,
152 freeaddrinfo(addresses);
159 static int handshake(int c)
164 r = write(c, buffer, 1);
167 r = write(c, "00000000000000", 14);
170 r = read(c, buffer, 1);
171 if (r != 1 && buffer[0] != 'E')
173 r = read(c, buffer, 14);
179 static void usage(void)
181 fprintf(stderr, "rnetclient [filename]\n");
185 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
188 /* Large files have to be uploaded as multiple
189 separately-deflated chunks, because the compressed and
190 uncompressed lengths in each record are encoded in unsigned
191 16-bit integers each.
193 The header can't be split into multiple chunks, and it
194 should never have to, since it won't ever get even close to
197 The uploaded file may be larger: to upload such large
198 files, it suffices to send multiple records till the entire
199 file is transferred, without waiting for a response. Since
200 we've alread informed the server of the file size in the
201 header, it knows exactly how much data to expect before
202 sending a response. It will only send an error message
203 before that if it times us out.
205 Odds are that any reasonably large size will do, but it
206 can't be too close to 64KiB, otherwise there won't be room
207 for the compressed length should it not compress well,
208 which should never happen for capital-ASCII-only
209 declaration files, but who knows?
211 This chunk size worked at the first try, uploading a
212 ~100KiB file, so let's stick with it. */
213 const int maxc = 64472;
214 if (header && len > maxc)
220 size_t clen = len < maxc ? len : maxc;
221 r = deflateRecord(buffer, clen, &out, &olen, header);
223 size_t n = gnutls_record_send(session, out, olen);
234 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
241 rnet_message_expand(message, 6);
242 buffer = (*message)->buffer;
243 r = gnutls_record_recv(session, buffer, 6);
244 if (buffer[0] == 0x01) {
245 len = chars2len(buffer+1);
246 rnet_message_expand(message, len);
247 buffer = (*message)->buffer + 6;
248 r = gnutls_record_recv(session, buffer, len);
249 inflateRecord(buffer - 6, len + 6, &out, &olen);
250 rnet_message_del(*message);
252 rnet_message_expand(message, olen);
253 memcpy((*message)->buffer, out, olen);
254 (*message)->len = olen;
257 len = chars2len(buffer+1);
258 rnet_message_expand(message, len - 1);
259 buffer = (*message)->buffer + 6;
260 r = gnutls_record_recv(session, buffer, len - 1);
261 (*message)->len = len + 4;
262 rnet_message_strip(*message, 4);
267 static void save_rec_file(char *cpf, char *buffer, int len)
275 home = getenv("HOME");
277 tmpdir = getenv("TMPDIR");
282 fnlen = strlen(home) + strlen(cpf) + 13;
283 filename = malloc(fnlen);
284 snprintf(filename, fnlen, "%s/%s.REC.XXXXXX", home, cpf);
286 fd = mkstemp(filename);
288 fprintf(stderr, "Could not create receipt file: %s\n",
292 r = write(fd, buffer, len);
294 fprintf(stderr, "Could not write to receipt file%s%s\n",
296 r < 0 ? strerror(errno) : "");
299 fprintf(stderr, "Wrote the receipt to %s.\n", filename);
306 static void handle_response_text_and_file(char *cpf, struct rnet_message *message)
310 if (!rnet_message_parse(message, "texto", &value, &vlen))
311 fprintf(stderr, "%.*s\n", vlen, value);
312 if (!rnet_message_parse(message, "arquivo", &value, &vlen))
313 save_rec_file(cpf, value, vlen);
316 static void handle_response_already_found(char *cpf, struct rnet_message *message)
318 handle_response_text_and_file(cpf, message);
321 static void handle_response_error(struct rnet_message *message)
325 if (!rnet_message_parse(message, "texto", &value, &vlen))
326 fprintf(stderr, "%.*s\n", vlen, value);
327 fprintf(stderr, "Error transmiting DEC file.\n");
330 int main(int argc, char **argv)
334 struct rnet_decfile *decfile;
335 struct rnet_message *message = NULL;
336 gnutls_session_t session;
344 decfile = rnet_decfile_open(argv[1]);
346 fprintf(stderr, "could not parse %s: %s\n", argv[1], strerror(errno));
350 cpf = rnet_decfile_get_header_field(decfile, "cpf");
352 gnutls_global_init();
354 session_new(&session);
355 r = connect_rnet(&c);
357 fprintf(stderr, "error connecting to server: %s\n",
358 r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
361 gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
366 if ((r = gnutls_handshake(session)) < 0)
367 fprintf(stderr, "error in handshake: %s\n",
370 rnet_encode(decfile, &message);
371 rnet_send(session, message->buffer, message->len, 1);
372 rnet_message_del(message);
375 r = rnet_recv(session, &message);
376 if (r || !message || message->len == 0) {
377 fprintf(stderr, "error when receiving response\n");
380 switch (message->buffer[0]) {
381 case 1: /* go ahead */
382 handle_response_text_and_file(cpf, message);
385 handle_response_error(message);
389 handle_response_already_found(cpf, message);
394 handle_response_text_and_file(cpf, message);
398 rnet_message_del(message);
403 message = rnet_decfile_get_file(decfile);
404 rnet_send(session, message->buffer, message->len, 0);
407 r = rnet_recv(session, &message);
408 if (r || !message || message->len == 0) {
409 fprintf(stderr, "error when receiving response\n");
412 switch (message->buffer[0]) {
414 handle_response_error(message);
420 handle_response_text_and_file(cpf, message);
425 gnutls_bye(session, GNUTLS_SHUT_RDWR);
427 rnet_decfile_close(decfile);
428 gnutls_global_deinit();