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]";
55 /* Description and definition of each option accepted by the program. */
57 static const struct argp_option rnetclient_options_desc[] = {
58 { "declaration", 'd', "FILE", 0,
59 "The Income Tax Report file that will be sent.",
62 { "output-dir", 'o', "DIRECTORY", 0,
63 "The directory where you wish to save the receipt.",
69 struct rnetclient_args {
70 /* File representing the declaration. */
73 /* Output directory to save the receipt. */
76 /* Output filename. */
77 char output_file[PATH_MAX];
80 /* Parser for command line arguments. */
82 static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state)
84 struct rnetclient_args *a = state->input;
87 /* The user has explicitly provided a filename through
97 /* The user has possibly provided a filename without
98 using any switches (e.g., by running './rnetclient
104 /* We have reached the end of the argument parsing.
105 Let's check if the user has provided a filename. */
106 if (a->input_file == NULL)
108 "You need to provide the Income Tax Declaration "
115 /* Control struct used by argp. */
117 static struct argp rnetclient_argp = {
118 rnetclient_options_desc,
119 rnetclient_parse_opt,
125 static size_t chars2len (unsigned char buf[2]) {
126 return (buf[0] << 8 | buf[1]);
129 static void * get_creds(char *certfile)
131 static gnutls_certificate_credentials_t cred;
132 gnutls_certificate_allocate_credentials(&cred);
133 gnutls_certificate_set_x509_trust_file(cred, certfile,
134 GNUTLS_X509_FMT_PEM);
138 static void session_new(gnutls_session_t *session)
141 cred = get_creds("cert.pem");
142 gnutls_init(session, GNUTLS_CLIENT);
143 gnutls_set_default_priority(*session);
144 gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, cred);
147 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen, int header)
151 zstrm.zalloc = Z_NULL;
152 zstrm.zfree = Z_NULL;
153 zstrm.opaque = Z_NULL;
154 if ((r = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION)) != Z_OK)
156 *out = malloc(len * 2 + 36);
161 zstrm.next_in = (z_const Bytef *) buffer;
162 zstrm.avail_in = len;
163 zstrm.next_out = (Bytef *) *out + 6;
164 zstrm.avail_out = len * 2 + 30;
165 while ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
166 zstrm.avail_out > 0);
167 if ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
172 *olen = zstrm.total_out + 6;
174 (*out)[1] = (zstrm.total_out >> 8);
175 (*out)[2] = (zstrm.total_out & 0xff);
176 (*out)[3] = (len >> 8);
177 (*out)[4] = (len & 0xff);
178 (*out)[5] = header ? 0x01 : 0x0;
183 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
187 zstrm.zalloc = Z_NULL;
188 zstrm.zfree = Z_NULL;
189 zstrm.opaque = Z_NULL;
190 if ((r = inflateInit(&zstrm)) != Z_OK)
192 *olen = chars2len((unsigned char *) buffer+3);
193 *out = malloc(*olen);
198 zstrm.next_in = (z_const Bytef *) buffer + 6;
199 zstrm.avail_in = len - 6;
200 zstrm.next_out = (Bytef *) *out;
201 zstrm.avail_out = *olen;
202 while ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
203 zstrm.avail_out > 0);
204 if ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
213 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
215 static int connect_rnet(int *c)
217 struct addrinfo *addresses;
218 struct addrinfo *addr;
219 struct addrinfo hint;
222 memset(&hint, 0, sizeof(hint));
223 hint.ai_family = AF_UNSPEC;
224 hint.ai_socktype = SOCK_STREAM;
225 hint.ai_protocol = IPPROTO_TCP;
226 hint.ai_flags = AI_ADDRCONFIG;
227 r = getaddrinfo(RNET_ADDRESS, "3456", &hint, &addresses);
231 for (addr = addresses; addr != NULL; addr = addr->ai_next) {
232 fd = socket(addr->ai_family, addr->ai_socktype,
235 if (!(r = connect(fd, addr->ai_addr,
241 freeaddrinfo(addresses);
248 static int handshake(int c)
253 r = write(c, buffer, 1);
256 r = write(c, "00000000000000", 14);
259 r = read(c, buffer, 1);
260 if (r != 1 && buffer[0] != 'E')
262 r = read(c, buffer, 14);
268 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
271 /* Large files have to be uploaded as multiple
272 separately-deflated chunks, because the compressed and
273 uncompressed lengths in each record are encoded in unsigned
274 16-bit integers each.
276 The header can't be split into multiple chunks, and it
277 should never have to, since it won't ever get even close to
280 The uploaded file may be larger: to upload such large
281 files, it suffices to send multiple records till the entire
282 file is transferred, without waiting for a response. Since
283 we've already informed the server of the file size in the
284 header, it knows exactly how much data to expect before
285 sending a response. It will only send an error message
286 before that if it times us out.
288 Odds are that any reasonably large size will do, but it
289 can't be too close to 64KiB, otherwise there won't be room
290 for the compressed length should it not compress well,
291 which should never happen for capital-ASCII-only
292 declaration files, but who knows?
294 This chunk size worked at the first try, uploading a
295 ~100KiB file, so let's stick with it. */
296 const unsigned int maxc = 64472;
297 if (header && len > maxc)
303 size_t clen = len < maxc ? len : maxc;
304 r = deflateRecord(buffer, clen, &out, &olen, header);
306 size_t n = gnutls_record_send(session, out, olen);
317 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
323 rnet_message_expand(message, 6);
324 buffer = (*message)->buffer;
325 gnutls_record_recv(session, buffer, 6);
326 if (buffer[0] == 0x01) {
327 len = chars2len((unsigned char *) buffer+1);
328 rnet_message_expand(message, len);
329 buffer = (*message)->buffer + 6;
330 gnutls_record_recv(session, buffer, len);
331 inflateRecord(buffer - 6, len + 6, &out, &olen);
332 rnet_message_del(*message);
334 rnet_message_expand(message, olen);
335 memcpy((*message)->buffer, out, olen);
336 (*message)->len = olen;
339 len = chars2len((unsigned char *) buffer+1);
340 rnet_message_expand(message, len - 1);
341 buffer = (*message)->buffer + 6;
342 gnutls_record_recv(session, buffer, len - 1);
343 (*message)->len = len + 4;
344 rnet_message_strip(*message, 4);
349 static int open_rec_file(char *cpf, struct rnetclient_args *args)
354 path = args->output_dir;
356 /* Now it's time to decide which filename to write. We use
357 the declaration's filename as a base layout, because the
358 proprietary version of the IRPF program only recognizes
359 receipts if they have the same name as the declaration
360 files (disconsidering the extensions). For example, if the
361 declaration file is named "123.DEC", the receipt should be
362 named "123.REC". Therefore, if the declaration file has
363 the ".DEC" extension, we strip it out and add the ".REC".
364 Otherwise, we use the default template, which is to save
365 the receipt with the name "$CPF.REC". */
366 tmp = strstr(args->input_file, ".DEC");
367 if (tmp != NULL && tmp[sizeof(".DEC") - 1] == '\0') {
369 /* We found the ".REC" extension. */
370 p = strdup(basename(args->input_file));
371 /* Replacing the ".DEC" by ".REC". Fortunately, we
372 just have to change one letter. */
373 tmp = strstr(p, ".DEC");
375 snprintf(args->output_file, PATH_MAX, "%s/%s", path, p);
378 /* The declaration filename does not follow the
379 convention, so we will not use it as a template.
380 We just generate a filename using "$CPF.REC". */
381 snprintf(args->output_file, PATH_MAX, "%s/%s.REC", path, cpf);
383 /* Now, open the file and write. */
384 fd = open(args->output_file, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
386 fprintf(stderr, "Could not create receipt file \"%s\": %s\n",
387 args->output_file, strerror(errno));
392 static int save_rec_file(int fd, char *buffer, int len, const struct rnetclient_args *args)
397 r = write(fd, buffer, len);
398 } while (r < 0 && errno == EAGAIN);
400 fprintf(stderr, "Could not write to receipt file: %s",
403 fprintf(stderr, "Wrote the receipt file to %s.\n",
409 static void handle_response_text_and_file(int fd, struct rnet_message *message, const struct rnetclient_args *args)
413 if (!rnet_message_parse(message, "texto", &value, &vlen))
414 fprintf(stderr, "%.*s\n", vlen, value);
415 if (!rnet_message_parse(message, "arquivo", &value, &vlen))
416 save_rec_file(fd, value, vlen, args);
419 static void handle_response_already_found(int fd, struct rnet_message *message, const struct rnetclient_args *args)
421 handle_response_text_and_file(fd, message, args);
424 static void handle_response_error(struct rnet_message *message)
428 if (!rnet_message_parse(message, "texto", &value, &vlen))
429 fprintf(stderr, "%.*s\n", vlen, value);
430 fprintf(stderr, "Error transmiting DEC file.\n");
433 int main(int argc, char **argv)
437 struct rnet_decfile *decfile;
438 struct rnet_message *message = NULL;
439 struct rnetclient_args rnet_args;
440 gnutls_session_t session;
447 /* Parsing the command line arguments. The argp_parse
448 function calls exit() if there is some error during the
449 parsing process (e.g., the user has provided an unknown
450 flag or the parsing function has called argp_error).
451 However, if our internal parsing function returns something
452 different than zero, then argp_parse returns this value to
453 us. This is a bug, and should not happen in the current
455 memset(&rnet_args, 0, sizeof (rnet_args));
456 err = argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args);
458 fprintf(stderr, "internal error while parsing command line arguments.");
460 /* If the user provided the output directory where she wishes
461 to save the receipt, then we use it. Otherwise, we save
462 the file in the current working directory (CWD). */
463 if (rnet_args.output_dir == NULL) {
464 rnet_args.output_dir = getcwd(cwd, PATH_MAX);
467 if (stat(rnet_args.output_dir, &st) < 0) {
468 fprintf(stderr, "Could not stat directory \"%s\": %s\n",
469 rnet_args.output_dir, strerror(errno));
472 if (!S_ISDIR(st.st_mode)) {
473 fprintf(stderr, "Error: \"%s\" is a not a directory.\n",
474 rnet_args.output_dir);
479 decfile = rnet_decfile_open(rnet_args.input_file);
481 fprintf(stderr, "could not parse file \"%s\": %s\n", rnet_args.input_file, strerror(errno));
485 cpf = rnet_decfile_get_header_field(decfile, "cpf");
487 outfd = open_rec_file(cpf, &rnet_args);
493 gnutls_global_init();
495 session_new(&session);
496 r = connect_rnet(&c);
498 fprintf(stderr, "error connecting to server: %s\n",
499 r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
503 gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
509 if ((r = gnutls_handshake(session)) < 0) {
510 fprintf(stderr, "error in handshake: %s\n",
516 r = rnet_encode(decfile, &message);
518 fprintf(stderr, "error encoding message, file not supported?\n");
523 rnet_send(session, message->buffer, message->len, 1);
524 rnet_message_del(message);
527 r = rnet_recv(session, &message);
528 if (r || !message || message->len == 0) {
529 fprintf(stderr, "error when receiving response\n");
533 switch (message->buffer[0]) {
534 case 1: /* go ahead */
535 handle_response_text_and_file(outfd, message, &rnet_args);
538 handle_response_error(message);
542 handle_response_already_found(outfd, message, &rnet_args);
547 handle_response_text_and_file(outfd, message, &rnet_args);
551 rnet_message_del(message);
556 message = rnet_decfile_get_file(decfile);
557 rnet_send(session, message->buffer, message->len, 0);
560 r = rnet_recv(session, &message);
561 if (r || !message || message->len == 0) {
562 fprintf(stderr, "error when receiving response\n");
566 switch (message->buffer[0]) {
568 handle_response_error(message);
574 handle_response_text_and_file(outfd, message, &rnet_args);
579 gnutls_bye(session, GNUTLS_SHUT_RDWR);
583 gnutls_global_deinit();
586 rnet_decfile_close(decfile);