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.
25 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 #include <gnutls/gnutls.h>
38 #include "rnet_message.h"
39 #include "rnet_encode.h"
41 /* Program version and bug report address. */
43 const char *argp_program_version = PACKAGE_VERSION;
44 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
46 /* Documentation strings. */
48 static const char rnetclient_doc[] =
49 "Send the Brazilian Income Tax Report to the Brazilian "
51 static const char rnetclient_args_doc[] =
52 "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]";
54 /* Description and definition of each option accepted by the program. */
56 static const struct argp_option rnetclient_options_desc[] = {
57 { "declaration", 'd', "FILE", 0,
58 "The Income Tax Report file that will be sent.",
61 { "output-dir", 'o', "DIRECTORY", 0,
62 "The directory where you wish to save the receipt.",
68 struct rnetclient_args {
69 /* File representing the declaration. */
72 /* Output directory to save the receipt. */
76 /* Parser for command line arguments. */
78 static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state)
80 struct rnetclient_args *a = state->input;
83 /* The user has explicitly provided a filename through
93 /* The user has possibly provided a filename without
94 using any switches (e.g., by running './rnetclient
100 /* We have reached the end of the argument parsing.
101 Let's check if the user has provided a filename. */
102 if (a->input_file == NULL)
104 "You need to provide the Income Tax Declaration "
111 /* Control struct used by argp. */
113 static struct argp rnetclient_argp = {
114 rnetclient_options_desc,
115 rnetclient_parse_opt,
121 static size_t chars2len (unsigned char buf[2]) {
122 return (buf[0] << 8 | buf[1]);
125 static void * get_creds(char *certfile)
127 static gnutls_certificate_credentials_t cred;
128 gnutls_certificate_allocate_credentials(&cred);
129 gnutls_certificate_set_x509_trust_file(cred, certfile,
130 GNUTLS_X509_FMT_PEM);
134 static void session_new(gnutls_session_t *session)
137 cred = get_creds("cert.pem");
138 gnutls_init(session, GNUTLS_CLIENT);
139 gnutls_set_default_priority(*session);
140 gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, cred);
143 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen, int header)
147 zstrm.zalloc = Z_NULL;
148 zstrm.zfree = Z_NULL;
149 zstrm.opaque = Z_NULL;
150 if ((r = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION)) != Z_OK)
152 *out = malloc(len * 2 + 36);
157 zstrm.next_in = (z_const Bytef *) buffer;
158 zstrm.avail_in = len;
159 zstrm.next_out = (Bytef *) *out + 6;
160 zstrm.avail_out = len * 2 + 30;
161 while ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
162 zstrm.avail_out > 0);
163 if ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
168 *olen = zstrm.total_out + 6;
170 (*out)[1] = (zstrm.total_out >> 8);
171 (*out)[2] = (zstrm.total_out & 0xff);
172 (*out)[3] = (len >> 8);
173 (*out)[4] = (len & 0xff);
174 (*out)[5] = header ? 0x01 : 0x0;
179 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
183 zstrm.zalloc = Z_NULL;
184 zstrm.zfree = Z_NULL;
185 zstrm.opaque = Z_NULL;
186 if ((r = inflateInit(&zstrm)) != Z_OK)
188 *olen = chars2len((unsigned char *) buffer+3);
189 *out = malloc(*olen);
194 zstrm.next_in = (z_const Bytef *) buffer + 6;
195 zstrm.avail_in = len - 6;
196 zstrm.next_out = (Bytef *) *out;
197 zstrm.avail_out = *olen;
198 while ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
199 zstrm.avail_out > 0);
200 if ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
209 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
211 static int connect_rnet(int *c)
213 struct addrinfo *addresses;
214 struct addrinfo *addr;
215 struct addrinfo hint;
218 memset(&hint, 0, sizeof(hint));
219 hint.ai_family = AF_UNSPEC;
220 hint.ai_socktype = SOCK_STREAM;
221 hint.ai_protocol = IPPROTO_TCP;
222 hint.ai_flags = AI_ADDRCONFIG;
223 r = getaddrinfo(RNET_ADDRESS, "3456", &hint, &addresses);
227 for (addr = addresses; addr != NULL; addr = addr->ai_next) {
228 fd = socket(addr->ai_family, addr->ai_socktype,
231 if (!(r = connect(fd, addr->ai_addr,
237 freeaddrinfo(addresses);
244 static int handshake(int c)
249 r = write(c, buffer, 1);
252 r = write(c, "00000000000000", 14);
255 r = read(c, buffer, 1);
256 if (r != 1 && buffer[0] != 'E')
258 r = read(c, buffer, 14);
264 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
267 /* Large files have to be uploaded as multiple
268 separately-deflated chunks, because the compressed and
269 uncompressed lengths in each record are encoded in unsigned
270 16-bit integers each.
272 The header can't be split into multiple chunks, and it
273 should never have to, since it won't ever get even close to
276 The uploaded file may be larger: to upload such large
277 files, it suffices to send multiple records till the entire
278 file is transferred, without waiting for a response. Since
279 we've alread informed the server of the file size in the
280 header, it knows exactly how much data to expect before
281 sending a response. It will only send an error message
282 before that if it times us out.
284 Odds are that any reasonably large size will do, but it
285 can't be too close to 64KiB, otherwise there won't be room
286 for the compressed length should it not compress well,
287 which should never happen for capital-ASCII-only
288 declaration files, but who knows?
290 This chunk size worked at the first try, uploading a
291 ~100KiB file, so let's stick with it. */
292 const unsigned int maxc = 64472;
293 if (header && len > maxc)
299 size_t clen = len < maxc ? len : maxc;
300 r = deflateRecord(buffer, clen, &out, &olen, header);
302 size_t n = gnutls_record_send(session, out, olen);
313 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
319 rnet_message_expand(message, 6);
320 buffer = (*message)->buffer;
321 gnutls_record_recv(session, buffer, 6);
322 if (buffer[0] == 0x01) {
323 len = chars2len((unsigned char *) buffer+1);
324 rnet_message_expand(message, len);
325 buffer = (*message)->buffer + 6;
326 gnutls_record_recv(session, buffer, len);
327 inflateRecord(buffer - 6, len + 6, &out, &olen);
328 rnet_message_del(*message);
330 rnet_message_expand(message, olen);
331 memcpy((*message)->buffer, out, olen);
332 (*message)->len = olen;
335 len = chars2len((unsigned char *) buffer+1);
336 rnet_message_expand(message, len - 1);
337 buffer = (*message)->buffer + 6;
338 gnutls_record_recv(session, buffer, len - 1);
339 (*message)->len = len + 4;
340 rnet_message_strip(*message, 4);
345 static void save_rec_file(char *cpf, char *buffer, int len, const struct rnetclient_args *args)
349 char *path, *fname, *tmp;
352 /* If the user provided the output directory where she wishes
353 to save the receipt, then we use it. Otherwise, we save
354 the file in the current working directory (CWD). */
355 if (args->output_dir == NULL)
356 path = getcwd(cwd, PATH_MAX);
359 if (stat(args->output_dir, &st) < 0) {
360 fprintf(stderr, "Could not stat directory \"%s\": %s\n", args->output_dir, strerror(errno));
363 if (!S_ISDIR(st.st_mode)) {
364 fprintf(stderr, "Error: \"%s\" is a not a directory.\n", args->output_dir);
367 path = args->output_dir;
369 /* Now it's time to decide which filename to write. We use
370 the declaration's filename as a base layout, because the
371 proprietary version of the IRPF program only recognizes
372 receipts if they have the same name as the declaration
373 files (disconsidering the extensions). For example, if the
374 declaration file is named "123.DEC", the receipt should be
375 named "123.REC". Therefore, if the declaration file has
376 the ".DEC" extension, we strip it out and add the ".REC".
377 Otherwise, we use the default template, which is to save
378 the receipt with the name "$CPF.REC". */
379 tmp = strstr(args->input_file, ".DEC");
380 if (tmp != NULL && tmp[sizeof(".DEC") - 1] == '\0') {
382 /* We found the ".REC" extension. */
383 p = strdup(args->input_file);
384 /* Replacing the ".DEC" by ".REC". Fortunately, we
385 just have to change one letter. */
386 tmp = strstr(p, ".DEC");
388 fname_len = strlen(p) + strlen(path) + 2;
389 fname = alloca(fname_len);
390 snprintf(fname, fname_len, "%s/%s", path, p);
392 /* The declaration filename does not follow the
393 convention, so we will not use it as a template.
394 We just generate a filename using "$CPF.REC". */
395 fname_len = strlen(cpf) + strlen(path) + sizeof(".REC") + 2;
396 fname = alloca(fname_len);
397 snprintf(fname, fname_len, "%s/%s.REC", path, cpf);
399 /* Now, open the file and write. */
400 fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
402 fprintf(stderr, "Could not create receipt file \"%s\": %s\n", fname, strerror(errno));
406 r = write(fd, buffer, len);
407 } while (r < 0 && errno == EAGAIN);
409 fprintf(stderr, "Could not write to receipt file: %s", strerror(errno));
411 fprintf(stderr, "Wrote the receipt file to %s.\n", fname);
415 static void handle_response_text_and_file(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
419 if (!rnet_message_parse(message, "texto", &value, &vlen))
420 fprintf(stderr, "%.*s\n", vlen, value);
421 if (!rnet_message_parse(message, "arquivo", &value, &vlen))
422 save_rec_file(cpf, value, vlen, args);
425 static void handle_response_already_found(char *cpf, struct rnet_message *message, const struct rnetclient_args *args)
427 handle_response_text_and_file(cpf, message, args);
430 static void handle_response_error(struct rnet_message *message)
434 if (!rnet_message_parse(message, "texto", &value, &vlen))
435 fprintf(stderr, "%.*s\n", vlen, value);
436 fprintf(stderr, "Error transmiting DEC file.\n");
439 int main(int argc, char **argv)
443 struct rnet_decfile *decfile;
444 struct rnet_message *message = NULL;
445 struct rnetclient_args rnet_args;
446 gnutls_session_t session;
451 /* Parsing the command line arguments. The argp_parse
452 function calls exit() if there is some error during the
453 parsing process (e.g., the user has provided an unknown
454 flag or the parsing function has called argp_error).
455 However, if our internal parsing function returns something
456 different than zero, then argp_parse returns this value to
457 us. This is a bug, and should not happen in the current
459 memset(&rnet_args, 0, sizeof (rnet_args));
460 err = argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args);
462 fprintf(stderr, "internal error while parsing command line arguments.");
464 decfile = rnet_decfile_open(rnet_args.input_file);
466 fprintf(stderr, "could not parse file \"%s\": %s\n", rnet_args.input_file, strerror(errno));
470 cpf = rnet_decfile_get_header_field(decfile, "cpf");
472 gnutls_global_init();
474 session_new(&session);
475 r = connect_rnet(&c);
477 fprintf(stderr, "error connecting to server: %s\n",
478 r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
481 gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
486 if ((r = gnutls_handshake(session)) < 0)
487 fprintf(stderr, "error in handshake: %s\n",
490 rnet_encode(decfile, &message);
491 rnet_send(session, message->buffer, message->len, 1);
492 rnet_message_del(message);
495 r = rnet_recv(session, &message);
496 if (r || !message || message->len == 0) {
497 fprintf(stderr, "error when receiving response\n");
500 switch (message->buffer[0]) {
501 case 1: /* go ahead */
502 handle_response_text_and_file(cpf, message, &rnet_args);
505 handle_response_error(message);
509 handle_response_already_found(cpf, message, &rnet_args);
514 handle_response_text_and_file(cpf, message, &rnet_args);
518 rnet_message_del(message);
523 message = rnet_decfile_get_file(decfile);
524 rnet_send(session, message->buffer, message->len, 0);
527 r = rnet_recv(session, &message);
528 if (r || !message || message->len == 0) {
529 fprintf(stderr, "error when receiving response\n");
532 switch (message->buffer[0]) {
534 handle_response_error(message);
540 handle_response_text_and_file(cpf, message, &rnet_args);
545 gnutls_bye(session, GNUTLS_SHUT_RDWR);
547 rnet_decfile_close(decfile);
548 gnutls_global_deinit();