2 * Copyright (C) 2012-2014 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
3 * Copyright (C) 2014 Alexandre Oliva <lxoliva@fsfla.org>
4 * Copyright (C) 2014 Sergio Durigan Junior <sergiodj@sergiodj.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
34 #include <gnutls/gnutls.h>
39 #include "rnet_message.h"
40 #include "rnet_encode.h"
42 /* Program version and bug report address. */
44 const char *argp_program_version = PACKAGE_VERSION;
45 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
47 /* Documentation strings. */
49 static const char rnetclient_doc[] =
50 "Send the Brazilian Income Tax Report to the Brazilian "
52 static const char rnetclient_args_doc[] =
53 "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]"
54 " [-s|--server-name SERVER]";
56 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
58 /* Description and definition of each option accepted by the program. */
60 static const struct argp_option rnetclient_options_desc[] = {
61 { "declaration", 'd', "FILE", 0,
62 "The Income Tax Report file that will be sent.",
65 { "output-dir", 'o', "DIRECTORY", 0,
66 "The directory where you wish to save the receipt.",
69 { "server-name", 's', "SERVER", 0,
70 "The server to connect to. Default is " RNET_ADDRESS ".",
76 struct rnetclient_args {
77 /* File representing the declaration. */
80 /* Output directory to save the receipt. */
83 /* Output filename. */
84 char output_file[PATH_MAX];
86 /* Server to connect to. */
90 /* Parser for command line arguments. */
92 static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state)
94 struct rnetclient_args *a = state->input;
97 /* The user has explicitly provided a filename through
107 a->server_name = arg;
111 /* The user has possibly provided a filename without
112 using any switches (e.g., by running './rnetclient
118 /* We have reached the end of the argument parsing.
119 Let's check if the user has provided a filename. */
120 if (a->input_file == NULL)
122 "You need to provide the Income Tax Declaration "
129 /* Control struct used by argp. */
131 static struct argp rnetclient_argp = {
132 rnetclient_options_desc,
133 rnetclient_parse_opt,
139 static size_t chars2len (unsigned char buf[2]) {
140 return (buf[0] << 8 | buf[1]);
143 static void * get_creds(char *certfile)
145 static gnutls_certificate_credentials_t cred;
146 gnutls_certificate_allocate_credentials(&cred);
147 gnutls_certificate_set_x509_trust_file(cred, certfile,
148 GNUTLS_X509_FMT_PEM);
152 static void session_new(gnutls_session_t *session)
155 cred = get_creds("cert.pem");
156 gnutls_init(session, GNUTLS_CLIENT);
157 gnutls_set_default_priority(*session);
158 gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, cred);
161 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen, int header)
165 zstrm.zalloc = Z_NULL;
166 zstrm.zfree = Z_NULL;
167 zstrm.opaque = Z_NULL;
168 if ((r = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION)) != Z_OK)
170 *out = malloc(len * 2 + 36);
175 zstrm.next_in = (z_const Bytef *) buffer;
176 zstrm.avail_in = len;
177 zstrm.next_out = (Bytef *) *out + 6;
178 zstrm.avail_out = len * 2 + 30;
179 while ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
180 zstrm.avail_out > 0);
181 if ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
186 *olen = zstrm.total_out + 6;
188 (*out)[1] = (zstrm.total_out >> 8);
189 (*out)[2] = (zstrm.total_out & 0xff);
190 (*out)[3] = (len >> 8);
191 (*out)[4] = (len & 0xff);
192 (*out)[5] = header ? 0x01 : 0x0;
197 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
201 zstrm.zalloc = Z_NULL;
202 zstrm.zfree = Z_NULL;
203 zstrm.opaque = Z_NULL;
204 if ((r = inflateInit(&zstrm)) != Z_OK)
206 *olen = chars2len((unsigned char *) buffer+3);
207 *out = malloc(*olen);
212 zstrm.next_in = (z_const Bytef *) buffer + 6;
213 zstrm.avail_in = len - 6;
214 zstrm.next_out = (Bytef *) *out;
215 zstrm.avail_out = *olen;
216 while ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
217 zstrm.avail_out > 0);
218 if ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
227 static int connect_rnet(int *c, char *server_name)
229 struct addrinfo *addresses;
230 struct addrinfo *addr;
231 struct addrinfo hint;
234 memset(&hint, 0, sizeof(hint));
235 hint.ai_family = AF_UNSPEC;
236 hint.ai_socktype = SOCK_STREAM;
237 hint.ai_protocol = IPPROTO_TCP;
238 hint.ai_flags = AI_ADDRCONFIG;
239 r = getaddrinfo(server_name, "3456", &hint, &addresses);
243 for (addr = addresses; addr != NULL; addr = addr->ai_next) {
244 fd = socket(addr->ai_family, addr->ai_socktype,
247 if (!(r = connect(fd, addr->ai_addr,
253 freeaddrinfo(addresses);
260 static int handshake(int c)
265 r = write(c, buffer, 1);
268 r = write(c, "00000000000000", 14);
271 r = read(c, buffer, 1);
272 if (r != 1 && buffer[0] != 'E')
274 r = read(c, buffer, 14);
280 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
283 /* Large files have to be uploaded as multiple
284 separately-deflated chunks, because the compressed and
285 uncompressed lengths in each record are encoded in unsigned
286 16-bit integers each.
288 The header can't be split into multiple chunks, and it
289 should never have to, since it won't ever get even close to
292 The uploaded file may be larger: to upload such large
293 files, it suffices to send multiple records till the entire
294 file is transferred, without waiting for a response. Since
295 we've already informed the server of the file size in the
296 header, it knows exactly how much data to expect before
297 sending a response. It will only send an error message
298 before that if it times us out.
300 Odds are that any reasonably large size will do, but it
301 can't be too close to 64KiB, otherwise there won't be room
302 for the compressed length should it not compress well,
303 which should never happen for capital-ASCII-only
304 declaration files, but who knows?
306 This chunk size worked at the first try, uploading a
307 ~100KiB file, so let's stick with it. */
308 const unsigned int maxc = 64472;
309 if (header && len > maxc)
315 size_t clen = len < maxc ? len : maxc;
316 r = deflateRecord(buffer, clen, &out, &olen, header);
318 size_t n = gnutls_record_send(session, out, olen);
329 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
335 rnet_message_expand(message, 6);
336 buffer = (*message)->buffer;
337 gnutls_record_recv(session, buffer, 6);
338 if (buffer[0] == 0x01) {
339 len = chars2len((unsigned char *) buffer+1);
340 rnet_message_expand(message, len);
341 buffer = (*message)->buffer + 6;
342 gnutls_record_recv(session, buffer, len);
343 inflateRecord(buffer - 6, len + 6, &out, &olen);
344 rnet_message_del(*message);
346 rnet_message_expand(message, olen);
347 memcpy((*message)->buffer, out, olen);
348 (*message)->len = olen;
351 len = chars2len((unsigned char *) buffer+1);
352 rnet_message_expand(message, len - 1);
353 buffer = (*message)->buffer + 6;
354 gnutls_record_recv(session, buffer, len - 1);
355 (*message)->len = len + 4;
356 rnet_message_strip(*message, 4);
361 static int open_rec_file(char *cpf, struct rnetclient_args *args)
366 path = args->output_dir;
368 /* Now it's time to decide which filename to write. We use
369 the declaration's filename as a base layout, because the
370 proprietary version of the IRPF program only recognizes
371 receipts if they have the same name as the declaration
372 files (disconsidering the extensions). For example, if the
373 declaration file is named "123.DEC", the receipt should be
374 named "123.REC". Therefore, if the declaration file has
375 the ".DEC" extension, we strip it out and add the ".REC".
376 Otherwise, we use the default template, which is to save
377 the receipt with the name "$CPF.REC". */
378 tmp = strstr(args->input_file, ".DEC");
379 if (tmp != NULL && tmp[sizeof(".DEC") - 1] == '\0') {
381 /* We found the ".REC" extension. */
382 p = strdup(basename(args->input_file));
383 /* Replacing the ".DEC" by ".REC". Fortunately, we
384 just have to change one letter. */
385 tmp = strstr(p, ".DEC");
387 snprintf(args->output_file, PATH_MAX, "%s/%s", path, p);
390 /* The declaration filename does not follow the
391 convention, so we will not use it as a template.
392 We just generate a filename using "$CPF.REC". */
393 snprintf(args->output_file, PATH_MAX, "%s/%s.REC", path, cpf);
395 /* Now, open the file and write. */
396 fd = open(args->output_file, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
398 fprintf(stderr, "Could not create receipt file \"%s\": %s\n",
399 args->output_file, strerror(errno));
404 static int save_rec_file(int fd, char *buffer, int len, const struct rnetclient_args *args)
409 r = write(fd, buffer, len);
410 } while (r < 0 && errno == EAGAIN);
412 fprintf(stderr, "Could not write to receipt file: %s",
415 fprintf(stderr, "Wrote the receipt file to %s.\n",
421 static void handle_response_text_and_file(int fd, struct rnet_message *message, const struct rnetclient_args *args)
425 if (!rnet_message_parse(message, "texto", &value, &vlen))
426 fprintf(stderr, "%.*s\n", vlen, value);
427 if (!rnet_message_parse(message, "arquivo", &value, &vlen))
428 save_rec_file(fd, value, vlen, args);
431 static void handle_response_already_found(int fd, struct rnet_message *message, const struct rnetclient_args *args)
433 handle_response_text_and_file(fd, message, args);
436 static void handle_response_error(struct rnet_message *message)
440 if (!rnet_message_parse(message, "texto", &value, &vlen))
441 fprintf(stderr, "%.*s\n", vlen, value);
442 fprintf(stderr, "Error transmiting DEC file.\n");
445 int main(int argc, char **argv)
449 struct rnet_decfile *decfile;
450 struct rnet_message *message = NULL;
451 struct rnetclient_args rnet_args;
452 gnutls_session_t session;
459 /* Parsing the command line arguments. The argp_parse
460 function calls exit() if there is some error during the
461 parsing process (e.g., the user has provided an unknown
462 flag or the parsing function has called argp_error).
463 However, if our internal parsing function returns something
464 different than zero, then argp_parse returns this value to
465 us. This is a bug, and should not happen in the current
467 memset(&rnet_args, 0, sizeof (rnet_args));
468 rnet_args.server_name = RNET_ADDRESS;
469 err = argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args);
471 fprintf(stderr, "internal error while parsing command line arguments.");
473 /* If the user provided the output directory where she wishes
474 to save the receipt, then we use it. Otherwise, we save
475 the file in the current working directory (CWD). */
476 if (rnet_args.output_dir == NULL) {
477 rnet_args.output_dir = getcwd(cwd, PATH_MAX);
480 if (stat(rnet_args.output_dir, &st) < 0) {
481 fprintf(stderr, "Could not stat directory \"%s\": %s\n",
482 rnet_args.output_dir, strerror(errno));
485 if (!S_ISDIR(st.st_mode)) {
486 fprintf(stderr, "Error: \"%s\" is a not a directory.\n",
487 rnet_args.output_dir);
492 decfile = rnet_decfile_open(rnet_args.input_file);
494 fprintf(stderr, "could not parse file \"%s\": %s\n", rnet_args.input_file, strerror(errno));
498 cpf = rnet_decfile_get_header_field(decfile, "cpf");
500 outfd = open_rec_file(cpf, &rnet_args);
506 gnutls_global_init();
508 session_new(&session);
509 r = connect_rnet(&c, rnet_args.server_name);
511 fprintf(stderr, "error connecting to server: %s\n",
512 r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
516 gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
522 if ((r = gnutls_handshake(session)) < 0) {
523 fprintf(stderr, "error in handshake: %s\n",
529 r = rnet_encode(decfile, &message);
531 fprintf(stderr, "error encoding message, file not supported?\n");
536 rnet_send(session, message->buffer, message->len, 1);
537 rnet_message_del(message);
540 r = rnet_recv(session, &message);
541 if (r || !message || message->len == 0) {
542 fprintf(stderr, "error when receiving response\n");
546 switch (message->buffer[0]) {
547 case 1: /* go ahead */
548 handle_response_text_and_file(outfd, message, &rnet_args);
551 handle_response_error(message);
555 handle_response_already_found(outfd, message, &rnet_args);
560 handle_response_text_and_file(outfd, message, &rnet_args);
564 rnet_message_del(message);
569 message = rnet_decfile_get_file(decfile);
570 rnet_send(session, message->buffer, message->len, 0);
573 r = rnet_recv(session, &message);
574 if (r || !message || message->len == 0) {
575 fprintf(stderr, "error when receiving response\n");
579 switch (message->buffer[0]) {
581 handle_response_error(message);
587 handle_response_text_and_file(outfd, message, &rnet_args);
592 gnutls_bye(session, GNUTLS_SHUT_RDWR);
596 gnutls_global_deinit();
599 rnet_decfile_close(decfile);