Release 2017.1
[cascardo/rnetclient.git] / rnetclient.c
1 /*
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>
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 #define _GNU_SOURCE
22 #include <string.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 #include <gnutls/gnutls.h>
35 #include <zlib.h>
36 #include <argp.h>
37 #include "config.h"
38 #include "decfile.h"
39 #include "rnet_message.h"
40 #include "rnet_encode.h"
41
42 /* Program version and bug report address.  */
43
44 const char *argp_program_version = PACKAGE_VERSION;
45 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
46
47 /* Documentation strings.  */
48
49 static const char rnetclient_doc[] =
50         "Send the Brazilian Income Tax Report to the Brazilian "
51         "Tax Authority";
52 static const char rnetclient_args_doc[] =
53         "[-d|--declaration] FILE [-o|--output-dir DIRECTORY]"
54         " [-s|--server-name SERVER]"
55         " [-c|--client-version CLIENT]";
56
57 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
58
59 /* Description and definition of each option accepted by the program.  */
60
61 static const struct argp_option rnetclient_options_desc[] = {
62         { "declaration", 'd', "FILE", 0,
63           "The Income Tax Report file that will be sent.",
64           0 },
65
66         { "output-dir", 'o', "DIRECTORY", 0,
67           "The directory where you wish to save the receipt.",
68           0 },
69
70         { "server-name", 's', "SERVER", 0,
71           "The server to connect to. Default is " RNET_ADDRESS ".",
72           0 },
73
74         { "client-version", 'c', "CLIENT", 0,
75           "The client version to be used.",
76           0 },
77
78         { NULL },
79 };
80
81 struct rnetclient_args {
82         /* File representing the declaration.  */
83         char *input_file;
84
85         /* Output directory to save the receipt.  */
86         char *output_dir;
87
88         /* Output filename. */
89         char output_file[PATH_MAX];
90
91         /* Server to connect to. */
92         char *server_name;
93
94         /* Client version to use. */
95         char *client_version;
96 };
97
98 /* Parser for command line arguments.  */
99
100 static error_t rnetclient_parse_opt(int key, char *arg, struct argp_state *state)
101 {
102         struct rnetclient_args *a = state->input;
103         switch (key) {
104         case 'd':
105                 /* The user has explicitly provided a filename through
106                    the '-d' switch.  */
107                 a->input_file = arg;
108                 break;
109
110         case 'o':
111                 a->output_dir = arg;
112                 break;
113
114         case 's':
115                 a->server_name = arg;
116                 break;
117
118         case 'c':
119                 a->client_version = arg;
120                 break;
121
122         case ARGP_KEY_ARG:
123                 /* The user has possibly provided a filename without
124                    using any switches (e.g., by running './rnetclient
125                    file').  */
126                 a->input_file = arg;
127                 break;
128
129         case ARGP_KEY_END:
130                 /* We have reached the end of the argument parsing.
131                    Let's check if the user has provided a filename.  */
132                 if (a->input_file == NULL)
133                         argp_error(state,
134                                    "You need to provide the Income Tax Declaration "
135                                    "filename.");
136         }
137
138         return 0;
139 }
140
141 /* Control struct used by argp.  */
142
143 static struct argp rnetclient_argp = {
144         rnetclient_options_desc,
145         rnetclient_parse_opt,
146         rnetclient_args_doc,
147         rnetclient_doc,
148         NULL, NULL, NULL
149 };
150
151 static size_t chars2len (unsigned char buf[2]) {
152         return (buf[0] << 8 | buf[1]);
153 }
154
155 static void * get_creds(char *certfile)
156 {
157         static gnutls_certificate_credentials_t cred;
158         gnutls_certificate_allocate_credentials(&cred);
159         gnutls_certificate_set_x509_trust_file(cred, certfile,
160                                         GNUTLS_X509_FMT_PEM);
161         return cred;
162 }
163
164 static void session_new(gnutls_session_t *session)
165 {
166         static void *cred;
167         cred = get_creds("cert.pem");
168         gnutls_init(session, GNUTLS_CLIENT);
169         gnutls_set_default_priority(*session);
170         gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, cred);
171 }
172
173 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen, int header)
174 {
175         z_stream zstrm;
176         int r;
177         zstrm.zalloc = Z_NULL;
178         zstrm.zfree = Z_NULL;
179         zstrm.opaque = Z_NULL;
180         if ((r = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION)) != Z_OK)
181                 return -1;
182         *out = malloc(len * 2 + 36);
183         if (!out) {
184                 deflateEnd(&zstrm);
185                 return -1;
186         }
187         zstrm.next_in = (z_const Bytef *) buffer;
188         zstrm.avail_in = len;
189         zstrm.next_out = (Bytef *) *out + 6;
190         zstrm.avail_out = len * 2 + 30;
191         while ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
192                 zstrm.avail_out > 0);
193         if ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
194                 deflateEnd(&zstrm);
195                 free(*out);
196                 return -1;
197         }
198         *olen = zstrm.total_out + 6;
199         (*out)[0] = 0x1;
200         (*out)[1] = (zstrm.total_out >> 8);
201         (*out)[2] = (zstrm.total_out & 0xff);
202         (*out)[3] = (len >> 8);
203         (*out)[4] = (len & 0xff);
204         (*out)[5] = header ? 0x01 : 0x0;
205         deflateEnd(&zstrm);
206         return 0;
207 }
208
209 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
210 {
211         z_stream zstrm;
212         int r;
213         zstrm.zalloc = Z_NULL;
214         zstrm.zfree = Z_NULL;
215         zstrm.opaque = Z_NULL;
216         if ((r = inflateInit(&zstrm)) != Z_OK)
217                 return -1;
218         *olen = chars2len((unsigned char *) buffer+3);
219         *out = malloc(*olen);
220         if (!out) {
221                 inflateEnd(&zstrm);
222                 return -1;
223         }
224         zstrm.next_in = (z_const Bytef *) buffer + 6;
225         zstrm.avail_in = len - 6;
226         zstrm.next_out = (Bytef *) *out;
227         zstrm.avail_out = *olen;
228         while ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
229                 zstrm.avail_out > 0);
230         if ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
231                 inflateEnd(&zstrm);
232                 free(*out);
233                 return -1;
234         }
235         inflateEnd(&zstrm);
236         return 0;
237 }
238
239 static int connect_rnet(int *c, char *server_name)
240 {
241         struct addrinfo *addresses;
242         struct addrinfo *addr;
243         struct addrinfo hint;
244         int r;
245         int fd = *c = -1;
246         memset(&hint, 0, sizeof(hint));
247         hint.ai_family = AF_UNSPEC;
248         hint.ai_socktype = SOCK_STREAM;
249         hint.ai_protocol = IPPROTO_TCP;
250         hint.ai_flags = AI_ADDRCONFIG;
251         r = getaddrinfo(server_name, "3456", &hint, &addresses);
252         if (r) {
253                 return r;
254         }
255         for (addr = addresses; addr != NULL; addr = addr->ai_next) {
256                 fd = socket(addr->ai_family, addr->ai_socktype,
257                                 addr->ai_protocol);
258                 if (fd >= 0)
259                         if (!(r = connect(fd, addr->ai_addr,
260                                                 addr->ai_addrlen)))
261                                 break;
262                 close(fd);
263                 fd = -1;
264         }
265         freeaddrinfo(addresses);
266         *c = fd;
267         if (fd == -1)
268                 return EAI_SYSTEM;
269         return 0;
270 }
271
272 static int handshake(int c)
273 {
274         char buffer[16];
275         int r;
276         buffer[0] = 1;
277         r = write(c, buffer, 1);
278         if (r < 1)
279                 return -1;
280         r = write(c, "00000000000000", 14);
281         if (r < 14)
282                 return -1;
283         r = read(c, buffer, 1);
284         if (r != 1 && buffer[0] != 'E')
285                 return -1;
286         r = read(c, buffer, 14);
287         if (r != 14)
288                 return -1;
289         return 0;
290 }
291
292 static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int header)
293 {
294         int r = 0;
295         /* Large files have to be uploaded as multiple
296            separately-deflated chunks, because the compressed and
297            uncompressed lengths in each record are encoded in unsigned
298            16-bit integers each.
299
300            The header can't be split into multiple chunks, and it
301            should never have to, since it won't ever get even close to
302            64KiB.
303
304            The uploaded file may be larger: to upload such large
305            files, it suffices to send multiple records till the entire
306            file is transferred, without waiting for a response.  Since
307            we've already informed the server of the file size in the
308            header, it knows exactly how much data to expect before
309            sending a response.  It will only send an error message
310            before that if it times us out.
311
312            Odds are that any reasonably large size will do, but it
313            can't be too close to 64KiB, otherwise there won't be room
314            for the compressed length should it not compress well,
315            which should never happen for capital-ASCII-only
316            declaration files, but who knows?
317
318            This chunk size worked at the first try, uploading a
319            ~100KiB file, so let's stick with it.  */
320         const unsigned int maxc = 64472;
321         if (header && len > maxc)
322                 return -1;
323
324         do {
325                 char *out = NULL;
326                 size_t olen;
327                 size_t clen = len < maxc ? len : maxc;
328                 r = deflateRecord(buffer, clen, &out, &olen, header);
329                 if (!r) {
330                         size_t n = gnutls_record_send(session, out, olen);
331                         if (n != olen)
332                                 r = -1;
333                 }
334                 free(out);
335                 buffer += clen;
336                 len -= clen;
337         } while (len && !r);
338         return r;
339 }
340
341 static int rnet_recv(gnutls_session_t session, struct rnet_message **message)
342 {
343         char *out = NULL;
344         size_t olen = 0;
345         char *buffer;
346         size_t len;
347         rnet_message_expand(message, 6);
348         buffer = (*message)->buffer;
349         gnutls_record_recv(session, buffer, 6);
350         if (buffer[0] == 0x01) {
351                 len = chars2len((unsigned char *) buffer+1);
352                 rnet_message_expand(message, len);
353                 buffer = (*message)->buffer + 6;
354                 gnutls_record_recv(session, buffer, len);
355                 inflateRecord(buffer - 6, len + 6, &out, &olen);
356                 rnet_message_del(*message);
357                 *message = NULL;
358                 rnet_message_expand(message, olen);
359                 memcpy((*message)->buffer, out, olen);
360                 (*message)->len = olen;
361                 free(out);
362         } else {
363                 len = chars2len((unsigned char *) buffer+1);
364                 rnet_message_expand(message, len - 1);
365                 buffer = (*message)->buffer + 6;
366                 gnutls_record_recv(session, buffer, len - 1);
367                 (*message)->len = len + 4;
368                 rnet_message_strip(*message, 4);
369         }
370         return 0;
371 }
372
373 static int open_rec_file(char *cpf, struct rnetclient_args *args)
374 {
375         int fd;
376         char *path, *tmp;
377
378         path = args->output_dir;
379
380         /* Now it's time to decide which filename to write.  We use
381            the declaration's filename as a base layout, because the
382            proprietary version of the IRPF program only recognizes
383            receipts if they have the same name as the declaration
384            files (disconsidering the extensions).  For example, if the
385            declaration file is named "123.DEC", the receipt should be
386            named "123.REC".  Therefore, if the declaration file has
387            the ".DEC" extension, we strip it out and add the ".REC".
388            Otherwise, we use the default template, which is to save
389            the receipt with the name "$CPF.REC".  */
390         tmp = strstr(args->input_file, ".DEC");
391         if (tmp != NULL && tmp[sizeof(".DEC") - 1] == '\0') {
392                 char *p;
393                 /* We found the ".REC" extension.  */
394                 p = strdup(basename(args->input_file));
395                 /* Replacing the ".DEC" by ".REC".  Fortunately, we
396                    just have to change one letter.  */
397                 tmp = strstr(p, ".DEC");
398                 tmp[1] = 'R';
399                 snprintf(args->output_file, PATH_MAX, "%s/%s", path, p);
400                 free(p);
401         } else {
402                 /* The declaration filename does not follow the
403                    convention, so we will not use it as a template.
404                    We just generate a filename using "$CPF.REC".  */
405                 snprintf(args->output_file, PATH_MAX, "%s/%s.REC", path, cpf);
406         }
407         /* Now, open the file and write.  */
408         fd = open(args->output_file, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
409         if (fd < 0) {
410                 fprintf(stderr, "Could not create receipt file \"%s\": %s\n",
411                         args->output_file, strerror(errno));
412         }
413         return fd;
414 }
415
416 static int save_rec_file(int fd, char *buffer, int len, const struct rnetclient_args *args)
417 {
418         ssize_t r;
419
420         do {
421                 r = write(fd, buffer, len);
422         } while (r < 0 && errno == EAGAIN);
423         if (r != len) {
424                 fprintf(stderr, "Could not write to receipt file: %s",
425                         strerror(errno));
426         } else {
427                 fprintf(stderr, "Wrote the receipt file to %s.\n",
428                         args->output_file);
429         }
430         return r;
431 }
432
433 static void handle_response_text_and_file(int fd, struct rnet_message *message, const struct rnetclient_args *args)
434 {
435         char *value;
436         int vlen;
437         if (!rnet_message_parse(message, "texto", &value, &vlen))
438                 fprintf(stderr, "%.*s\n", vlen, value);
439         if (!rnet_message_parse(message, "arquivo", &value, &vlen))
440                 save_rec_file(fd, value, vlen, args);
441 }
442
443 static void handle_response_already_found(int fd, struct rnet_message *message, const struct rnetclient_args *args)
444 {
445         handle_response_text_and_file(fd, message, args);
446 }
447
448 static void handle_response_error(struct rnet_message *message)
449 {
450         char *value;
451         int vlen;
452         if (!rnet_message_parse(message, "texto", &value, &vlen))
453                 fprintf(stderr, "%.*s\n", vlen, value);
454         fprintf(stderr, "Error transmiting DEC file.\n");
455 }
456
457 int main(int argc, char **argv)
458 {
459         int c;
460         int r = 0;
461         struct rnet_decfile *decfile;
462         struct rnet_message *message = NULL;
463         struct rnetclient_args rnet_args;
464         gnutls_session_t session;
465         int finish = 0;
466         char *cpf;
467         error_t err;
468         char cwd[PATH_MAX];
469         int outfd;
470
471         /* Parsing the command line arguments.  The argp_parse
472            function calls exit() if there is some error during the
473            parsing process (e.g., the user has provided an unknown
474            flag or the parsing function has called argp_error).
475            However, if our internal parsing function returns something
476            different than zero, then argp_parse returns this value to
477            us.  This is a bug, and should not happen in the current
478            state.  */
479         memset(&rnet_args, 0, sizeof (rnet_args));
480         rnet_args.server_name = RNET_ADDRESS;
481         rnet_args.client_version = RNET_DEFAULT_VERSION;
482         err = argp_parse (&rnetclient_argp, argc, argv, 0, NULL, &rnet_args);
483         if (err != 0)
484                 fprintf(stderr, "internal error while parsing command line arguments.");
485
486         /* If the user provided the output directory where she wishes
487            to save the receipt, then we use it.  Otherwise, we save
488            the file in the current working directory (CWD).  */
489         if (rnet_args.output_dir == NULL) {
490                 rnet_args.output_dir = getcwd(cwd, PATH_MAX);
491         } else {
492                 struct stat st;
493                 if (stat(rnet_args.output_dir, &st) < 0) {
494                         fprintf(stderr, "Could not stat directory \"%s\": %s\n",
495                                 rnet_args.output_dir, strerror(errno));
496                         exit(1);
497                 }
498                 if (!S_ISDIR(st.st_mode)) {
499                         fprintf(stderr, "Error: \"%s\" is a not a directory.\n",
500                                 rnet_args.output_dir);
501                         exit(1);
502                 }
503         }
504
505         decfile = rnet_decfile_open(rnet_args.input_file);
506         if (!decfile) {
507                 fprintf(stderr, "could not parse file \"%s\": %s\n", rnet_args.input_file, strerror(errno));
508                 exit(1);
509         }
510
511         cpf = rnet_decfile_get_header_field(decfile, "cpf");
512
513         outfd = open_rec_file(cpf, &rnet_args);
514         if (outfd < 0) {
515                 r = 1;
516                 goto out_rec;
517         }
518
519         gnutls_global_init();
520
521         session_new(&session);
522         r = connect_rnet(&c, rnet_args.server_name);
523         if (r) {
524                 fprintf(stderr, "error connecting to server: %s\n",
525                         r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
526                 r = 1;
527                 goto out_connect;
528         }
529         gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(intptr_t) c);
530         r = handshake(c);
531         if (r < 0) {
532                 r = 1;
533                 goto out_handshake;
534         }
535         if ((r = gnutls_handshake(session)) < 0) {
536                 fprintf(stderr, "error in handshake: %s\n",
537                                 gnutls_strerror(r));
538                 r = 1;
539                 goto out_handshake;
540         }
541
542         r = rnet_encode(decfile, &message, rnet_args.client_version);
543         if (r < 0) {
544                 fprintf(stderr, "error encoding message, file not supported?\n");
545                 r = 1;
546                 goto out;
547         }
548
549         rnet_send(session, message->buffer, message->len, 1);
550         rnet_message_del(message);
551
552         message = NULL;
553         r = rnet_recv(session, &message);
554         if (r || !message || message->len == 0) {
555                 fprintf(stderr, "error when receiving response\n");
556                 r = 1;
557                 goto out;
558         }
559         switch (message->buffer[0]) {
560         case 1: /* go ahead */
561                 handle_response_text_and_file(outfd, message, &rnet_args);
562                 break;
563         case 3: /* error */
564                 handle_response_error(message);
565                 finish = 1;
566                 break;
567         case 4:
568                 handle_response_already_found(outfd, message, &rnet_args);
569                 finish = 1;
570                 break;
571         case 2:
572         case 5:
573                 handle_response_text_and_file(outfd, message, &rnet_args);
574                 finish = 1;
575                 break;
576         }
577         rnet_message_del(message);
578
579         if (finish)
580                 goto out;
581
582         message = rnet_decfile_get_file(decfile);
583         rnet_send(session, message->buffer, message->len, 0);
584
585         message = NULL;
586         r = rnet_recv(session, &message);
587         if (r || !message || message->len == 0) {
588                 fprintf(stderr, "error when receiving response\n");
589                 r = 1;
590                 goto out;
591         }
592         switch (message->buffer[0]) {
593         case 3: /* error */
594                 handle_response_error(message);
595                 break;
596         case 2:
597         case 4:
598         case 5:
599         case 1:
600                 handle_response_text_and_file(outfd, message, &rnet_args);
601                 break;
602         }
603         
604 out:
605         gnutls_bye(session, GNUTLS_SHUT_RDWR);
606 out_handshake:
607         close(c);
608 out_connect:
609         gnutls_global_deinit();
610         close(outfd);
611 out_rec:
612         rnet_decfile_close(decfile);
613
614         return r;
615 }