Update copyright years
[cascardo/libreceita.git] / rnetclient.c
1 /*
2  *  Copyright (C) 2012-2013  Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include <string.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <netdb.h>
28 #include <gnutls/gnutls.h>
29 #include <zlib.h>
30
31 static void * get_creds(char *certfile)
32 {
33         static gnutls_certificate_credentials_t cred;
34         gnutls_certificate_allocate_credentials(&cred);
35         gnutls_certificate_set_x509_trust_file(cred, certfile,
36                                         GNUTLS_X509_FMT_PEM);
37         return cred;
38 }
39
40 static void session_new(gnutls_session_t *session)
41 {
42         static void *cred;
43         cred = get_creds("cert.pem");
44         gnutls_init(session, GNUTLS_CLIENT);
45         gnutls_set_default_priority(*session);
46         gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, cred);
47 }
48
49 static int deflateRecord(char *buffer, size_t len, char **out, size_t *olen)
50 {
51         z_stream zstrm;
52         int r;
53         zstrm.zalloc = Z_NULL;
54         zstrm.zfree = Z_NULL;
55         zstrm.opaque = Z_NULL;
56         if ((r = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION)) != Z_OK)
57                 return -1;
58         *out = malloc(len * 2 + 36);
59         if (!out) {
60                 deflateEnd(&zstrm);
61                 return -1;
62         }
63         zstrm.next_in = buffer;
64         zstrm.avail_in = len;
65         zstrm.next_out = *out + 6;
66         zstrm.avail_out = len * 2 + 30;
67         while ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
68                 zstrm.avail_out > 0);
69         if ((r = deflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
70                 deflateEnd(&zstrm);
71                 free(*out);
72                 return -1;
73         }
74         *olen = zstrm.avail_out + 6;
75         (*out)[0] = 0x1;
76         (*out)[1] = (zstrm.avail_out >> 8);
77         (*out)[2] = (zstrm.avail_out & 0xff);
78         (*out)[3] = (len >> 8);
79         (*out)[4] = (len & 0xff);
80         (*out)[5] = 0x1;
81         deflateEnd(&zstrm);
82         return 0;
83 }
84
85 static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen)
86 {
87         z_stream zstrm;
88         int r;
89         zstrm.zalloc = Z_NULL;
90         zstrm.zfree = Z_NULL;
91         zstrm.opaque = Z_NULL;
92         if ((r = inflateInit(&zstrm)) != Z_OK)
93                 return -1;
94         *olen = (buffer[3] << 8 & buffer[4]);
95         *out = malloc(*olen);
96         if (!out) {
97                 inflateEnd(&zstrm);
98                 return -1;
99         }
100         zstrm.next_in = buffer + 6;
101         zstrm.avail_in = len - 6;
102         zstrm.next_out = *out;
103         zstrm.avail_out = *olen;
104         while ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END &&
105                 zstrm.avail_out > 0);
106         if ((r = inflate(&zstrm, Z_FINISH)) != Z_STREAM_END) {
107                 inflateEnd(&zstrm);
108                 free(*out);
109                 return -1;
110         }
111         inflateEnd(&zstrm);
112         return 0;
113 }
114
115 #define RNET_ADDRESS "receitanet.receita.fazenda.gov.br"
116
117 static int connect_rnet(int *c)
118 {
119         struct addrinfo *addresses;
120         struct addrinfo *addr;
121         struct addrinfo hint;
122         struct sockaddr_in saddr;
123         int r;
124         int fd = *c = -1;
125         int i;
126         memset(&hint, 0, sizeof(hint));
127         hint.ai_family = AF_UNSPEC;
128         hint.ai_socktype = SOCK_STREAM;
129         hint.ai_protocol = IPPROTO_TCP;
130         hint.ai_flags = AI_ADDRCONFIG;
131         r = getaddrinfo(RNET_ADDRESS, "3456", &hint, &addresses);
132         if (r) {
133                 return r;
134         }
135         for (addr = addresses; addr != NULL; addr = addr->ai_next) {
136                 fd = socket(addr->ai_family, addr->ai_socktype,
137                                 addr->ai_protocol);
138                 if (fd >= 0)
139                         if (!(r = connect(fd, addr->ai_addr,
140                                                 addr->ai_addrlen)))
141                                 break;
142                 close(fd);
143                 fd = -1;
144         }
145         freeaddrinfo(addresses);
146         *c = fd;
147         if (fd == -1)
148                 return EAI_SYSTEM;
149         return 0;
150 }
151
152 static int handshake(int c)
153 {
154         char buffer[16];
155         int r;
156         buffer[0] = 1;
157         write(c, buffer, 1);
158         write(c, "00000000000000", 14);
159         r = read(c, buffer, 1);
160         if (r != 1 && buffer[0] != 'E')
161                 return -1;
162         r = read(c, buffer, 14);
163         if (r != 14)
164                 return -1;
165         return 0;
166 }
167
168 int main(int argc, char **argv)
169 {
170         int c;
171         int r;
172         char buffer[2048];
173         char *out;
174         size_t olen;
175         gnutls_session_t session;
176         gnutls_global_init();
177         session_new(&session);
178         r = connect_rnet(&c);
179         if (r) {
180                 fprintf(stderr, "error connecting to server: %s\n",
181                         r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
182                 exit(1);
183         }
184         gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) c);
185         r = handshake(c);
186         if (r < 0) {
187                 exit(1);
188         }
189         if ((r = gnutls_handshake(session)) < 0)
190                 fprintf(stderr, "error in handshake: %s\n",
191                                 gnutls_strerror(r));
192         else
193                 fprintf(stderr, "handshake ok\n");
194         r = read(0, buffer, sizeof(buffer));
195         deflateRecord(buffer, r, &out, &olen);
196         gnutls_record_send(session, out, olen);
197         free(out);
198         while ((r = gnutls_record_recv(session, buffer, sizeof(buffer))) > 0)
199                 write(1, buffer, r);
200         close(c);
201         gnutls_global_deinit();
202         return 0;
203 }