From f4103c33e85760d1096c6c9fa4220bd835203c83 Mon Sep 17 00:00:00 2001 From: "Adonay Felipe Nogueira (http://libreplanet.org/wiki/User:Adfeno)" Date: Tue, 2 Jun 2026 18:01:19 -0300 Subject: [PATCH] Add 2026 with warning, get DEC's MAC, JVM, connection's IP and port * decfile.c (decfile_parse_header): Add 2026. Ignore unused fields. * decfile.h: Add 2026. Commend on how to calculate size. * rnet_encode.c (rnet_cat, rnet_trim): New functions. (rnet_encode): Parse DEC's "mac_address", "jvm" as "versao_java"; use connection's IP, port; update "origem" and "so". * rnet_encode.h: Use same client version as reference. * rnet_message.c (add_field): Print chars as integers for easy debugging. * rnetclient.c (connect_rnet): Get connection's IP, port. (rnet_recv): Use fiscal year with placeholder and note the need for fixing the handling of the packets. (main): Get fiscal year and pass to rnet_recv, connection information pointer and pass to connect_rnet and rnet_encode. * t-parse.c: Adjustments for the above. --- decfile.c | 150 +++++++++---------------------------------------- decfile.h | 3 + rnet_encode.c | 86 ++++++++++++++++++++++++++-- rnet_encode.h | 10 +++- rnet_message.c | 7 +++ rnetclient.c | 50 ++++++++++++++--- t-parse.c | 22 +++++++- 7 files changed, 186 insertions(+), 142 deletions(-) diff --git a/decfile.c b/decfile.c index 3856c2b..6d4f694 100644 --- a/decfile.c +++ b/decfile.c @@ -108,16 +108,16 @@ static int decfile_parse_header(struct rnet_decfile *decfile) hash = decfile->header; /* Common header fields. Most of these are used by rnet_encode. */ - parse("sistema", 8); + p += 8; parse("exerc", 4); parse("ano", 4); parse("codigo_recnet", 4); parse("in_ret", 1); parse("cpf", 11); - parse("filler", 3); + p += 3; parse("tipo_ni", 1); parse("nr_versao", 3); - parse("nome", 60); + p += 60; parse("uf", 2); parse("hash", 10); @@ -137,145 +137,39 @@ static int decfile_parse_header(struct rnet_decfile *decfile) } /* Check for tested versions. */ - if (exerc > 2016) { + if (exerc > 2016 && exerc != 2026) { fprintf(stderr, "Unknown file version, but proceeding anyway.\n"); return 0; } + p += 23; + /* These fields exist at least since 2013. */ - parse("in_cert", 1); - parse("dt_nasc", 8); - parse("in_comp", 1); - parse("in_res", 1); - parse("in_gerada", 1); - parse("nr_recibo_anterior", 10); - parse("in_pgd", 1); parse("so", 14); parse("versao_so", 7); parse("jvm", 9); - parse("nr_recibo", 10); - parse("municipio", 4); - parse("conjuge", 11); - parse("obrig", 1); - parse("impdevido", 13); - parse("nr_recibo", 10); - parse("in_seg", 1); - parse("imppago", 2); - parse("impant", 1); - parse("mudend", 1); - parse("cep", 8); - parse("debito", 1); - parse("banco", 3); - parse("agencia", 4); - parse("filler", 1); - parse("data_julgado", 8); - parse("imppagar", 13); - parse("tribfonte", 1); - parse("cpfrra", 11); - parse("trib_rra", 1); - parse("cpf_rra2", 11); - parse("trib_3rra", 1); - parse("cpf_rra3", 11); - - /* Fields added in 2014. */ - if (exerc >= 2014) { - parse("trib_4rra", 1); - parse("cpf_rra4", 11); - } - /* These fields exist at least since 2013. */ - parse("vr_doacao", 13); - parse("cnpj1", 14); - parse("cnpj2", 14); - parse("cnpj3", 14); - parse("cnpj4", 14); - parse("cpf_dep1", 11); - parse("dnas_dep1", 8); - parse("cpf_dep2", 11); - parse("dnas_dep2", 8); - parse("cpf_dep3", 11); - parse("dnas_dep3", 8); - parse("cpf_dep4", 11); - parse("dnas_dep4", 8); - parse("cpf_dep5", 11); - parse("dnas_dep5", 8); - parse("cpf_dep6", 11); - parse("dnas_dep6", 8); - parse("cnpj_med1", 14); - parse("cnpj_med2", 14); - parse("cpf_alim", 11); - parse("cpf_invent", 11); - parse("municipio", 40); - parse("contribuinte", 60); - - /* The contents of this field until 2014 (cpf_empregada) were moved to - * the end of the header in 2015 (cpfdomestic@). This field has then - * been converted into a filler field. */ - if (exerc <= 2014) { - parse("cpf_empregada", 11); + if (exerc >= 2026) { + p += 497; + } else if (exerc >= 2014) { + p += 484; } else { - parse("filler", 11); + p += 472; } /* These fields exist at least since 2013. */ parse("mac", 12); - parse("data_nao_residente", 8); - parse("cpf_procurador", 11); - parse("obrigatoriedade", 3); - parse("rendtrib", 13); - parse("cnpj_prev", 14); - parse("cnpj_prev2", 14); - parse("vr_totisentos", 13); - parse("vr_totexclusivo", 13); - parse("vr_totpagamentos", 13); - - /* End of header in 2013. */ - - /* Fields added in 2014. */ - if (exerc >= 2014) { - parse("nr_conta", 13); - parse("nr_dv_conta", 2); - parse("in_dv_conta", 1); - } - - /* End of header in 2014. */ - - /* Fields added in 2015. */ - if (exerc >= 2015) { - parse("codnaturezaocup", 2); - parse("cpfdomestic@", 11); - parse("nitdomestic@", 11); - parse("cpfdomestic@2", 11); - parse("nitdomestic@2", 11); - parse("cpfdomestic@3", 11); - parse("nitdomestic@3", 11); - parse("deciniciada", 1); - parse("utilpgd", 1); - parse("utilapp", 1); - parse("utilonline", 1); - parse("utilrascunho", 1); - parse("utilprepreenchida", 1); - parse("utilfontes", 1); - parse("utilplanosaude", 1); - parse("utilrecuperar", 1); - parse("dectransmitida", 1); - } - /* End of header in 2015. */ - - /* Fields added in 2016. */ - if (exerc >= 2016) { - parse("dedutivelmaior1", 14); - parse("dedutivelmaior2", 14); - parse("dedutivelmaior3", 14); - parse("dedutivelmaior4", 14); - parse("dedutivelmaior5", 14); - parse("dedutivelmaior6", 14); - parse("funprespmaior", 14); + if (exerc >= 2026) { + p += 558; + } else if (exerc >= 2016) { + p += 294; + } else if (exerc >= 2015) { + p += 196; + } else if (exerc >= 2014) { + p += 118; } - /* End of header in 2016. */ - /* Tail fields, which exist at least since 2013. */ tail = p; parse("versaotestpgd", 3); @@ -327,6 +221,12 @@ static int decfile_parse_header(struct rnet_decfile *decfile) goto out_val; } break; + case 2026: + if (p - buffer != RNET_HEADER_SIZE_2026) { + fprintf(stderr, "RNET_HEADER_SIZE_2026 in decfile.h needs to be adjusted to %ti,\nor parse_header in decfile.c needs updating\n", p - buffer); + goto out_val; + } + break; default: /* This case should never be reached even for later years, because later years should not be parsed diff --git a/decfile.h b/decfile.h index b212c40..159f694 100644 --- a/decfile.h +++ b/decfile.h @@ -24,10 +24,13 @@ #define RNET_HEADER_HEAD_COMMON 111 #define RNET_HEADER_TAIL_COMMON 15 + +/* Characters + CR + LF + NULL - 1 (0th index = 1st char). */ #define RNET_HEADER_SIZE_2013 765 #define RNET_HEADER_SIZE_2014 793 #define RNET_HEADER_SIZE_2015 871 #define RNET_HEADER_SIZE_2016 969 +#define RNET_HEADER_SIZE_2026 1246 struct rnet_decfile; struct rnet_decfile * rnet_decfile_open(char *filename); diff --git a/rnet_encode.c b/rnet_encode.c index fefee79..1e2c66e 100644 --- a/rnet_encode.c +++ b/rnet_encode.c @@ -24,10 +24,52 @@ #include #include #include +#include +#include +#include +#include #include "rnet_message.h" #include "decfile.h" -int rnet_encode(struct rnet_decfile *decfile, struct rnet_message **msg, char *client) +int rnet_cat(char **destination, const char *source) { + if (destination == NULL || source == NULL) { + return 0; + } + + size_t current_length = (*destination != NULL) ? strlen(*destination) : 0; + size_t source_length = strlen(source); + size_t new_size = current_length + source_length + 1; + + char *temp = (char *) realloc(*destination, new_size); + if (temp == NULL) { + return 0; + } + *destination = temp; + + if (current_length == 0) { + (*destination)[0] = '\0'; + } + strncat(*destination, source, source_length); + return 1; +} + +void rnet_trim(char *str) { + char *start = str + strspn(str, " \t\n\r"); + char *end = str + strlen(str) - 1; + + while (end > start && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) { + --end; + } + + *(end + 1) = '\0'; + + if (start > str) { + // +2 = last char + NULL. + memmove(str, start, end - start + 2); + } +} + +int rnet_encode(struct rnet_decfile *decfile, struct rnet_message **msg, struct sockaddr_storage *connection, char *client) { int r = -EIO; @@ -38,12 +80,32 @@ int rnet_encode(struct rnet_decfile *decfile, struct rnet_message **msg, char *c char *ano; char *exerc; char *uf; + char *jvm; uint16_t versao_pgd; uint64_t file_len; char *hash; char *header; uint8_t ret; + socklen_t connection_info_size; + char connection_ip[INET6_ADDRSTRLEN]; + char connection_port_string[NI_MAXSERV]; + int connection_port; + char *decfile_mac; + unsigned int mac_array[6]; + char connection_mac[18]; + + connection_info_size = sizeof(*connection); + + r = getnameinfo((struct sockaddr *) connection, connection_info_size, + connection_ip, sizeof(connection_ip), + connection_port_string, sizeof(connection_port_string), + NI_NUMERICHOST | NI_NUMERICSERV); + if (r < 0) { + perror("getnameinfo cannot get client IP and port"); + goto out; + } + connection_port = atoi(connection_port_string); size_t header_size, header_head, header_tail; if (client == NULL) @@ -71,9 +133,21 @@ int rnet_encode(struct rnet_decfile *decfile, struct rnet_message **msg, char *c ano = rnet_decfile_get_header_field(decfile, "ano"); exerc = rnet_decfile_get_header_field(decfile, "exerc"); uf = rnet_decfile_get_header_field(decfile, "uf"); + + jvm = rnet_decfile_get_header_field(decfile, "jvm"); + rnet_trim(jvm); + rnet_cat(&jvm, ";OpenJDK Runtime Environment"); + versao_pgd = strtoul(rnet_decfile_get_header_field(decfile, "nr_versao"), NULL, 10); ret = strtoul(rnet_decfile_get_header_field(decfile, "in_ret"), NULL, 10); + decfile_mac = rnet_decfile_get_header_field(decfile, "mac"); + if (strlen(decfile_mac) != 12 || sscanf(decfile_mac, "%2x%2x%2x%2x%2x%2x", &mac_array[0], &mac_array[1], &mac_array[2], &mac_array[3], &mac_array[4], &mac_array[5]) != 6) { + fprintf(stderr, "Invalid MAC address inside DEC file: %s\n", decfile_mac); + goto out; + } + snprintf(connection_mac, sizeof(connection_mac), "%02X-%02X-%02X-%02X-%02X-%02X", mac_array[0], mac_array[1], mac_array[2], mac_array[3], mac_array[4], mac_array[5]); + (*msg)->buffer[0] = 0x40; (*msg)->len = 1; r = rnet_message_add_u32(msg, "a_comp", 0); @@ -95,10 +169,12 @@ int rnet_encode(struct rnet_decfile *decfile, struct rnet_message **msg, char *c r = rnet_message_add_u8(msg, "vrs_des_pa", 0); r = rnet_message_add_u16(msg, "versao_pgd", versao_pgd); r = rnet_message_add_u8(msg, "critica_validador", 0x06); - r = rnet_message_add_ascii(msg, "ip_loc", "127.0.0.1"); - r = rnet_message_add_ascii(msg, "versao_java", "1.5.0-gij;Free Software rnetclient pretending to be GNU Interpreter for Java"); - r = rnet_message_add_ascii(msg, "origem", "JA2R"); - r = rnet_message_add_ascii(msg, "so", "GNU"); + r = rnet_message_add_ascii(msg, "ip_loc", connection_ip); + r = rnet_message_add_u32(msg, "porta", connection_port); + r = rnet_message_add_ascii(msg, "mac_address", connection_mac); + r = rnet_message_add_ascii(msg, "versao_java", jvm); + r = rnet_message_add_ascii(msg, "origem", "JA2J"); + r = rnet_message_add_ascii(msg, "so", "Linux"); r = rnet_message_add_ascii(msg, "cliente", client); r = rnet_message_add_buffer(msg, "dados_val", header + header_head, diff --git a/rnet_encode.h b/rnet_encode.h index 6ebbd9f..17f1f08 100644 --- a/rnet_encode.h +++ b/rnet_encode.h @@ -19,11 +19,17 @@ #ifndef _RNET_ENCODE_H #define _RNET_ENCODE_H +#include +#include #include "rnet_message.h" #include "decfile.h" -#define RNET_DEFAULT_VERSION "201704" +#define RNET_DEFAULT_VERSION "201132" -int rnet_encode(struct rnet_decfile *decfile, struct rnet_message **msg, char *client); +int rnet_cat(char **destination, const char *source); + +void rnet_trim(char *str); + +int rnet_encode(struct rnet_decfile *decfile, struct rnet_message **msg, struct sockaddr_storage *connection, char *client); #endif diff --git a/rnet_message.c b/rnet_message.c index 95d8b41..a7ad17c 100644 --- a/rnet_message.c +++ b/rnet_message.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef MAX #define MAX(a,b) (a >= b) ? a : b @@ -71,6 +72,12 @@ int rnet_message_strip(struct rnet_message *message, size_t len) static int add_field(struct rnet_message **message, char *key, int klen, char *val, int vlen) { + for (int i = 0; i < klen; i++) + putchar (key[i]); + printf ("%s", ": ("); + for (int i = 0; i < vlen; i++) + printf ("%i,", (int)val[i]); + printf ("%s", ")\n"); int n = 0; char *buffer; struct rnet_message *msg = *message; diff --git a/rnetclient.c b/rnetclient.c index 756cee8..fcdaafa 100644 --- a/rnetclient.c +++ b/rnetclient.c @@ -236,29 +236,37 @@ static int inflateRecord(char *buffer, size_t len, char **out, size_t *olen) return 0; } -static int connect_rnet(int *c, char *server_name) +static int connect_rnet(int *c, struct sockaddr_storage *connection, char *server_name) { struct addrinfo *addresses; struct addrinfo *addr; struct addrinfo hint; int r; int fd = *c = -1; + socklen_t connection_info_size; memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_UNSPEC; hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = IPPROTO_TCP; hint.ai_flags = AI_ADDRCONFIG; r = getaddrinfo(server_name, "3456", &hint, &addresses); + connection_info_size = sizeof(*connection); + if (r) { return r; } for (addr = addresses; addr != NULL; addr = addr->ai_next) { fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); - if (fd >= 0) + if (fd >= 0) { if (!(r = connect(fd, addr->ai_addr, - addr->ai_addrlen))) + addr->ai_addrlen))) { + if (getsockname(fd, (struct sockaddr *) connection, &connection_info_size) == -1) { + perror("getsockname failed to get information on connection"); + } break; + } + } close(fd); fd = -1; } @@ -338,7 +346,7 @@ static int rnet_send(gnutls_session_t session, char *buffer, size_t len, int hea return r; } -static int rnet_recv(gnutls_session_t session, struct rnet_message **message) +static int rnet_recv(gnutls_session_t session, struct rnet_message **message, int exerc) { char *out = NULL; size_t olen = 0; @@ -347,6 +355,27 @@ static int rnet_recv(gnutls_session_t session, struct rnet_message **message) rnet_message_expand(message, 6); buffer = (*message)->buffer; gnutls_record_recv(session, buffer, 6); + if (exerc >= 2026) { + /* FIXME: The server response may have changed: + + * 5th packet, C (unchanged): summary + of connection data and of DEC file, + but not DEC itself. + + * 6th packet, S (maybe different from + doc/FORMATO): (4 bytes) + 0x01 0x0a + pos_inicio 0x00 (9 times) + + * 7th packet: + * If file already sent, S + (idem): 0x04 0x08 tipo_rec... + + * If not sent, C (still + needs review): client sends + DEC file. + */ + fprintf(stderr, "Sending for the fiscal year %d may give errors, but proceeding anyway.\n", exerc); + } if (buffer[0] == 0x01) { len = chars2len((unsigned char *) buffer+1); rnet_message_expand(message, len); @@ -462,8 +491,10 @@ int main(int argc, char **argv) struct rnet_message *message = NULL; struct rnetclient_args rnet_args; gnutls_session_t session; + struct sockaddr_storage connection; int finish = 0; char *cpf; + int exerc; error_t err; char cwd[PATH_MAX]; int outfd; @@ -509,6 +540,7 @@ int main(int argc, char **argv) } cpf = rnet_decfile_get_header_field(decfile, "cpf"); + exerc = atoi(rnet_decfile_get_header_field(decfile, "exerc")); outfd = open_rec_file(cpf, &rnet_args); if (outfd < 0) { @@ -519,7 +551,7 @@ int main(int argc, char **argv) gnutls_global_init(); session_new(&session); - r = connect_rnet(&c, rnet_args.server_name); + r = connect_rnet(&c, &connection, rnet_args.server_name); if (r) { fprintf(stderr, "error connecting to server: %s\n", r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r)); @@ -539,7 +571,7 @@ int main(int argc, char **argv) goto out_handshake; } - r = rnet_encode(decfile, &message, rnet_args.client_version); + r = rnet_encode(decfile, &message, &connection, rnet_args.client_version); if (r < 0) { fprintf(stderr, "error encoding message, file not supported?\n"); r = 1; @@ -550,7 +582,7 @@ int main(int argc, char **argv) rnet_message_del(message); message = NULL; - r = rnet_recv(session, &message); + r = rnet_recv(session, &message, exerc); if (r || !message || message->len == 0) { fprintf(stderr, "error when receiving response\n"); r = 1; @@ -583,7 +615,7 @@ int main(int argc, char **argv) rnet_send(session, message->buffer, message->len, 0); message = NULL; - r = rnet_recv(session, &message); + r = rnet_recv(session, &message, exerc); if (r || !message || message->len == 0) { fprintf(stderr, "error when receiving response\n"); r = 1; @@ -600,7 +632,7 @@ int main(int argc, char **argv) handle_response_text_and_file(outfd, message, &rnet_args); break; } - + out: gnutls_bye(session, GNUTLS_SHUT_RDWR); out_handshake: diff --git a/t-parse.c b/t-parse.c index ac735de..b61ce7f 100644 --- a/t-parse.c +++ b/t-parse.c @@ -17,6 +17,8 @@ */ #include +#include +#include #include "decfile.h" #include "rnet_encode.h" @@ -26,6 +28,24 @@ int main(void) int i, r; struct rnet_decfile *decfile; struct rnet_message *message = NULL; + + struct sockaddr_storage connection; + socklen_t connection_info_size; + + memset(&connection, 0, sizeof(connection)); + struct sockaddr_in *connection4 = (struct sockaddr_in *) &connection; + connection4->sin_family = AF_INET; + connection4->sin_port = htons(12345); + inet_pton(AF_INET, "127.0.0.1", &connection4->sin_addr); + connection_info_size = sizeof(connection); + + /* + char test_ip1 = "127.0.0.1"; + struct in_addr test_ip2 = inet_pton(AF_INET, test_ip1, &test_ip2); + int test_port1 = 12345; + in_port_t test_port2 = htons(test_port1); + */ + char *filename[4] = { SRCDIR"/data/12345678909-IRPF-A-2013-2012-ORIGI.DEC", SRCDIR"/data/12345678909-IRPF-A-2014-2013-ORIGI.DEC", @@ -41,7 +61,7 @@ int main(void) return -1; } - r = rnet_encode(decfile, &message, NULL); + r = rnet_encode(decfile, &message, &connection, NULL); if (r < 0) { fprintf(stderr, "Error encoding message\n"); return -1; -- 2.34.1