Commit c44dcfd5a2a5f56c8885b89f41d1519859a1815c
1 parent
15608980
Make the TCP implementation "protocol independent", i.e. IPv6 capable.
Inspired by the branch ff/ipv6 of Florian Forster but this version uses a specific PI backend to isolate the IPv4 and IPv6 implementations. The existing TCP/IPv4 implementation has been kept intact (not rebased on the PI one) because its data backend requires fewer bytes than the PI. New functions modbus_new_tcp_pi, modbus_tcp_pi_listen and modbus_tcp_pi_accept and new backend called modbus_tcp_pi_t.
Showing
6 changed files
with
336 additions
and
27 deletions
AUTHORS
src/modbus-tcp-private.h
| @@ -31,4 +31,16 @@ typedef struct _modbus_tcp { | @@ -31,4 +31,16 @@ typedef struct _modbus_tcp { | ||
| 31 | char ip[16]; | 31 | char ip[16]; |
| 32 | } modbus_tcp_t; | 32 | } modbus_tcp_t; |
| 33 | 33 | ||
| 34 | +#define _MODBUS_TCP_PI_NODE_LENGTH 1025 | ||
| 35 | +#define _MODBUS_TCP_PI_SERVICE_LENGTH 32 | ||
| 36 | + | ||
| 37 | +typedef struct _modbus_tcp_pi { | ||
| 38 | + /* TCP port */ | ||
| 39 | + int port; | ||
| 40 | + /* Node */ | ||
| 41 | + char node[_MODBUS_TCP_PI_NODE_LENGTH]; | ||
| 42 | + /* Service */ | ||
| 43 | + char service[_MODBUS_TCP_PI_SERVICE_LENGTH]; | ||
| 44 | +} modbus_tcp_pi_t; | ||
| 45 | + | ||
| 34 | #endif /* _MODBUS_TCP_PRIVATE_H_ */ | 46 | #endif /* _MODBUS_TCP_PRIVATE_H_ */ |
src/modbus-tcp.c
| @@ -42,6 +42,8 @@ | @@ -42,6 +42,8 @@ | ||
| 42 | # include <netinet/ip.h> | 42 | # include <netinet/ip.h> |
| 43 | # include <netinet/tcp.h> | 43 | # include <netinet/tcp.h> |
| 44 | # include <arpa/inet.h> | 44 | # include <arpa/inet.h> |
| 45 | +# include <poll.h> | ||
| 46 | +# include <netdb.h> | ||
| 45 | #endif | 47 | #endif |
| 46 | 48 | ||
| 47 | #if !defined(MSG_NOSIGNAL) | 49 | #if !defined(MSG_NOSIGNAL) |
| @@ -185,32 +187,17 @@ int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_lengt | @@ -185,32 +187,17 @@ int _modbus_tcp_check_integrity(modbus_t *ctx, uint8_t *msg, const int msg_lengt | ||
| 185 | return msg_length; | 187 | return msg_length; |
| 186 | } | 188 | } |
| 187 | 189 | ||
| 188 | -/* Establishes a modbus TCP connection with a Modbus server. */ | ||
| 189 | -static int _modbus_tcp_connect(modbus_t *ctx) | 190 | +static int _modbus_tcp_set_ipv4_options(int s) |
| 190 | { | 191 | { |
| 191 | int rc; | 192 | int rc; |
| 192 | int option; | 193 | int option; |
| 193 | - struct sockaddr_in addr; | ||
| 194 | - modbus_tcp_t *ctx_tcp = ctx->backend_data; | ||
| 195 | - | ||
| 196 | -#ifdef OS_WIN32 | ||
| 197 | - if (_modbus_tcp_init_win32() == -1) { | ||
| 198 | - return -1; | ||
| 199 | - } | ||
| 200 | -#endif | ||
| 201 | - | ||
| 202 | - ctx->s = socket(PF_INET, SOCK_STREAM, 0); | ||
| 203 | - if (ctx->s == -1) { | ||
| 204 | - return -1; | ||
| 205 | - } | ||
| 206 | 194 | ||
| 207 | /* Set the TCP no delay flag */ | 195 | /* Set the TCP no delay flag */ |
| 208 | /* SOL_TCP = IPPROTO_TCP */ | 196 | /* SOL_TCP = IPPROTO_TCP */ |
| 209 | option = 1; | 197 | option = 1; |
| 210 | - rc = setsockopt(ctx->s, IPPROTO_TCP, TCP_NODELAY, | 198 | + rc = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, |
| 211 | (const void *)&option, sizeof(int)); | 199 | (const void *)&option, sizeof(int)); |
| 212 | if (rc == -1) { | 200 | if (rc == -1) { |
| 213 | - close(ctx->s); | ||
| 214 | return -1; | 201 | return -1; |
| 215 | } | 202 | } |
| 216 | 203 | ||
| @@ -221,14 +208,40 @@ static int _modbus_tcp_connect(modbus_t *ctx) | @@ -221,14 +208,40 @@ static int _modbus_tcp_connect(modbus_t *ctx) | ||
| 221 | **/ | 208 | **/ |
| 222 | /* Set the IP low delay option */ | 209 | /* Set the IP low delay option */ |
| 223 | option = IPTOS_LOWDELAY; | 210 | option = IPTOS_LOWDELAY; |
| 224 | - rc = setsockopt(ctx->s, IPPROTO_IP, IP_TOS, | 211 | + rc = setsockopt(s, IPPROTO_IP, IP_TOS, |
| 225 | (const void *)&option, sizeof(int)); | 212 | (const void *)&option, sizeof(int)); |
| 226 | if (rc == -1) { | 213 | if (rc == -1) { |
| 227 | - close(ctx->s); | ||
| 228 | return -1; | 214 | return -1; |
| 229 | } | 215 | } |
| 230 | #endif | 216 | #endif |
| 231 | 217 | ||
| 218 | + return 0; | ||
| 219 | +} | ||
| 220 | + | ||
| 221 | +/* Establishes a modbus TCP connection with a Modbus server. */ | ||
| 222 | +static int _modbus_tcp_connect(modbus_t *ctx) | ||
| 223 | +{ | ||
| 224 | + int rc; | ||
| 225 | + struct sockaddr_in addr; | ||
| 226 | + modbus_tcp_t *ctx_tcp = ctx->backend_data; | ||
| 227 | + | ||
| 228 | +#ifdef OS_WIN32 | ||
| 229 | + if (_modbus_tcp_init_win32() == -1) { | ||
| 230 | + return -1; | ||
| 231 | + } | ||
| 232 | +#endif | ||
| 233 | + | ||
| 234 | + ctx->s = socket(PF_INET, SOCK_STREAM, 0); | ||
| 235 | + if (ctx->s == -1) { | ||
| 236 | + return -1; | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + rc = _modbus_tcp_set_ipv4_options(ctx->s); | ||
| 240 | + if (rc == -1) { | ||
| 241 | + close(ctx->s); | ||
| 242 | + return -1; | ||
| 243 | + } | ||
| 244 | + | ||
| 232 | if (ctx->debug) { | 245 | if (ctx->debug) { |
| 233 | printf("Connecting to %s\n", ctx_tcp->ip); | 246 | printf("Connecting to %s\n", ctx_tcp->ip); |
| 234 | } | 247 | } |
| @@ -246,6 +259,61 @@ static int _modbus_tcp_connect(modbus_t *ctx) | @@ -246,6 +259,61 @@ static int _modbus_tcp_connect(modbus_t *ctx) | ||
| 246 | return 0; | 259 | return 0; |
| 247 | } | 260 | } |
| 248 | 261 | ||
| 262 | +/* Establishes a modbus TCP PI connection with a Modbus server. */ | ||
| 263 | +static int _modbus_tcp_pi_connect(modbus_t *ctx) | ||
| 264 | +{ | ||
| 265 | + int rc; | ||
| 266 | + struct addrinfo *ai_list; | ||
| 267 | + struct addrinfo *ai_ptr; | ||
| 268 | + struct addrinfo ai_hints; | ||
| 269 | + modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; | ||
| 270 | + | ||
| 271 | + memset(&ai_hints, 0, sizeof(ai_hints)); | ||
| 272 | +#ifdef AI_ADDRCONFIG | ||
| 273 | + ai_hints.ai_flags |= AI_ADDRCONFIG; | ||
| 274 | +#endif | ||
| 275 | + ai_hints.ai_family = AF_UNSPEC; | ||
| 276 | + ai_hints.ai_socktype = SOCK_STREAM; | ||
| 277 | + ai_hints.ai_addr = NULL; | ||
| 278 | + ai_hints.ai_canonname = NULL; | ||
| 279 | + ai_hints.ai_next = NULL; | ||
| 280 | + | ||
| 281 | + ai_list = NULL; | ||
| 282 | + rc = getaddrinfo(ctx_tcp_pi->node, ctx_tcp_pi->service, | ||
| 283 | + &ai_hints, &ai_list); | ||
| 284 | + if (rc != 0) | ||
| 285 | + return rc; | ||
| 286 | + | ||
| 287 | + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { | ||
| 288 | + int s; | ||
| 289 | + | ||
| 290 | + s = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); | ||
| 291 | + if (s < 0) | ||
| 292 | + continue; | ||
| 293 | + | ||
| 294 | + if (ai_ptr->ai_family == AF_INET) | ||
| 295 | + _modbus_tcp_set_ipv4_options(s); | ||
| 296 | + | ||
| 297 | + rc = connect(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen); | ||
| 298 | + if (rc != 0) { | ||
| 299 | + close(s); | ||
| 300 | + continue; | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + ctx->s = s; | ||
| 304 | + break; | ||
| 305 | + } | ||
| 306 | + | ||
| 307 | + freeaddrinfo(ai_list); | ||
| 308 | + | ||
| 309 | + if (ctx->s < 0) { | ||
| 310 | + errno = ENOTCONN; | ||
| 311 | + return -1; | ||
| 312 | + } | ||
| 313 | + | ||
| 314 | + return 0; | ||
| 315 | +} | ||
| 316 | + | ||
| 249 | /* Closes the network connection and socket in TCP mode */ | 317 | /* Closes the network connection and socket in TCP mode */ |
| 250 | void _modbus_tcp_close(modbus_t *ctx) | 318 | void _modbus_tcp_close(modbus_t *ctx) |
| 251 | { | 319 | { |
| @@ -330,6 +398,98 @@ int modbus_tcp_listen(modbus_t *ctx, int nb_connection) | @@ -330,6 +398,98 @@ int modbus_tcp_listen(modbus_t *ctx, int nb_connection) | ||
| 330 | return new_socket; | 398 | return new_socket; |
| 331 | } | 399 | } |
| 332 | 400 | ||
| 401 | +int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection) | ||
| 402 | +{ | ||
| 403 | + int rc; | ||
| 404 | + struct addrinfo *ai_list; | ||
| 405 | + struct addrinfo *ai_ptr; | ||
| 406 | + struct addrinfo ai_hints; | ||
| 407 | + const char *node; | ||
| 408 | + const char *service; | ||
| 409 | + int new_socket; | ||
| 410 | + modbus_tcp_pi_t *ctx_tcp_pi = ctx->backend_data; | ||
| 411 | + | ||
| 412 | + if (ctx_tcp_pi->node[0] == 0) | ||
| 413 | + node = NULL; /* == any */ | ||
| 414 | + else | ||
| 415 | + node = ctx_tcp_pi->node; | ||
| 416 | + | ||
| 417 | + if (ctx_tcp_pi->service[0] == 0) | ||
| 418 | + service = "502"; | ||
| 419 | + else | ||
| 420 | + service = ctx_tcp_pi->service; | ||
| 421 | + | ||
| 422 | + memset(&ai_hints, 0, sizeof (ai_hints)); | ||
| 423 | + ai_hints.ai_flags |= AI_PASSIVE; | ||
| 424 | +#ifdef AI_ADDRCONFIG | ||
| 425 | + ai_hints.ai_flags |= AI_ADDRCONFIG; | ||
| 426 | +#endif | ||
| 427 | + ai_hints.ai_family = AF_UNSPEC; | ||
| 428 | + ai_hints.ai_socktype = SOCK_STREAM; | ||
| 429 | + ai_hints.ai_addr = NULL; | ||
| 430 | + ai_hints.ai_canonname = NULL; | ||
| 431 | + ai_hints.ai_next = NULL; | ||
| 432 | + | ||
| 433 | + ai_list = NULL; | ||
| 434 | + rc = getaddrinfo(node, service, &ai_hints, &ai_list); | ||
| 435 | + if (rc != 0) | ||
| 436 | + return -1; | ||
| 437 | + | ||
| 438 | + new_socket = -1; | ||
| 439 | + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { | ||
| 440 | + int s; | ||
| 441 | + | ||
| 442 | + s = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, | ||
| 443 | + ai_ptr->ai_protocol); | ||
| 444 | + if (s < 0) { | ||
| 445 | + if (ctx->debug) { | ||
| 446 | + perror("socket"); | ||
| 447 | + } | ||
| 448 | + continue; | ||
| 449 | + } else { | ||
| 450 | + int yes = 1; | ||
| 451 | + rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, | ||
| 452 | + (void *) &yes, sizeof (yes)); | ||
| 453 | + if (rc != 0) { | ||
| 454 | + close(s); | ||
| 455 | + if (ctx->debug) { | ||
| 456 | + perror("setsockopt"); | ||
| 457 | + } | ||
| 458 | + continue; | ||
| 459 | + } | ||
| 460 | + } | ||
| 461 | + | ||
| 462 | + rc = bind(s, ai_ptr->ai_addr, ai_ptr->ai_addrlen); | ||
| 463 | + if (rc != 0) { | ||
| 464 | + close(s); | ||
| 465 | + if (ctx->debug) { | ||
| 466 | + perror("bind"); | ||
| 467 | + } | ||
| 468 | + continue; | ||
| 469 | + } | ||
| 470 | + | ||
| 471 | + rc = listen(s, nb_connection); | ||
| 472 | + if (rc != 0) { | ||
| 473 | + close(s); | ||
| 474 | + if (ctx->debug) { | ||
| 475 | + perror("listen"); | ||
| 476 | + } | ||
| 477 | + continue; | ||
| 478 | + } | ||
| 479 | + | ||
| 480 | + new_socket = s; | ||
| 481 | + break; | ||
| 482 | + } | ||
| 483 | + freeaddrinfo(ai_list); | ||
| 484 | + | ||
| 485 | + if (new_socket < 0) { | ||
| 486 | + errno = ENOTCONN; | ||
| 487 | + return -1; | ||
| 488 | + } | ||
| 489 | + | ||
| 490 | + return new_socket; | ||
| 491 | +} | ||
| 492 | + | ||
| 333 | /* On success, the function return a non-negative integer that is a descriptor | 493 | /* On success, the function return a non-negative integer that is a descriptor |
| 334 | for the accepted socket. On error, -1 is returned, and errno is set | 494 | for the accepted socket. On error, -1 is returned, and errno is set |
| 335 | appropriately. */ | 495 | appropriately. */ |
| @@ -338,7 +498,7 @@ int modbus_tcp_accept(modbus_t *ctx, int *socket) | @@ -338,7 +498,7 @@ int modbus_tcp_accept(modbus_t *ctx, int *socket) | ||
| 338 | struct sockaddr_in addr; | 498 | struct sockaddr_in addr; |
| 339 | socklen_t addrlen; | 499 | socklen_t addrlen; |
| 340 | 500 | ||
| 341 | - addrlen = sizeof(struct sockaddr_in); | 501 | + addrlen = sizeof(addr); |
| 342 | ctx->s = accept(*socket, (struct sockaddr *)&addr, &addrlen); | 502 | ctx->s = accept(*socket, (struct sockaddr *)&addr, &addrlen); |
| 343 | if (ctx->s == -1) { | 503 | if (ctx->s == -1) { |
| 344 | close(*socket); | 504 | close(*socket); |
| @@ -347,7 +507,27 @@ int modbus_tcp_accept(modbus_t *ctx, int *socket) | @@ -347,7 +507,27 @@ int modbus_tcp_accept(modbus_t *ctx, int *socket) | ||
| 347 | } | 507 | } |
| 348 | 508 | ||
| 349 | if (ctx->debug) { | 509 | if (ctx->debug) { |
| 350 | - printf("The client %s is connected\n", inet_ntoa(addr.sin_addr)); | 510 | + printf("The client connection from %s is accepted\n", |
| 511 | + inet_ntoa(addr.sin_addr)); | ||
| 512 | + } | ||
| 513 | + | ||
| 514 | + return ctx->s; | ||
| 515 | +} | ||
| 516 | + | ||
| 517 | +int modbus_tcp_pi_accept(modbus_t *ctx, int *socket) | ||
| 518 | +{ | ||
| 519 | + struct sockaddr_storage addr; | ||
| 520 | + socklen_t addrlen; | ||
| 521 | + | ||
| 522 | + addrlen = sizeof(addr); | ||
| 523 | + ctx->s = accept(*socket, (void *)&addr, &addrlen); | ||
| 524 | + if (ctx->s == -1) { | ||
| 525 | + close(*socket); | ||
| 526 | + *socket = 0; | ||
| 527 | + } | ||
| 528 | + | ||
| 529 | + if (ctx->debug) { | ||
| 530 | + printf("The client connection is accepted."); | ||
| 351 | } | 531 | } |
| 352 | 532 | ||
| 353 | return ctx->s; | 533 | return ctx->s; |
| @@ -412,9 +592,29 @@ const modbus_backend_t _modbus_tcp_backend = { | @@ -412,9 +592,29 @@ const modbus_backend_t _modbus_tcp_backend = { | ||
| 412 | }; | 592 | }; |
| 413 | 593 | ||
| 414 | 594 | ||
| 595 | +const modbus_backend_t _modbus_tcp_pi_backend = { | ||
| 596 | + _MODBUS_BACKEND_TYPE_TCP, | ||
| 597 | + _MODBUS_TCP_HEADER_LENGTH, | ||
| 598 | + _MODBUS_TCP_CHECKSUM_LENGTH, | ||
| 599 | + MODBUS_TCP_MAX_ADU_LENGTH, | ||
| 600 | + _modbus_set_slave, | ||
| 601 | + _modbus_tcp_build_request_basis, | ||
| 602 | + _modbus_tcp_build_response_basis, | ||
| 603 | + _modbus_tcp_prepare_response_tid, | ||
| 604 | + _modbus_tcp_send_msg_pre, | ||
| 605 | + _modbus_tcp_send, | ||
| 606 | + _modbus_tcp_recv, | ||
| 607 | + _modbus_tcp_check_integrity, | ||
| 608 | + _modbus_tcp_pi_connect, | ||
| 609 | + _modbus_tcp_close, | ||
| 610 | + _modbus_tcp_flush, | ||
| 611 | + _modbus_tcp_select, | ||
| 612 | + _modbus_tcp_filter_request | ||
| 613 | +}; | ||
| 614 | + | ||
| 415 | /* Allocates and initializes the modbus_t structure for TCP. | 615 | /* Allocates and initializes the modbus_t structure for TCP. |
| 416 | - - ip : "192.168.0.5" | ||
| 417 | - - port : 1099 | 616 | + - ip: '192.168.0.5' |
| 617 | + - port: 1099 | ||
| 418 | 618 | ||
| 419 | Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one | 619 | Set the port to MODBUS_TCP_DEFAULT_PORT to use the default one |
| 420 | (502). It's convenient to use a port number greater than or equal | 620 | (502). It's convenient to use a port number greater than or equal |
| @@ -425,6 +625,8 @@ modbus_t* modbus_new_tcp(const char *ip, int port) | @@ -425,6 +625,8 @@ modbus_t* modbus_new_tcp(const char *ip, int port) | ||
| 425 | { | 625 | { |
| 426 | modbus_t *ctx; | 626 | modbus_t *ctx; |
| 427 | modbus_tcp_t *ctx_tcp; | 627 | modbus_tcp_t *ctx_tcp; |
| 628 | + size_t dest_size; | ||
| 629 | + size_t ret_size; | ||
| 428 | 630 | ||
| 429 | #if defined(OS_BSD) | 631 | #if defined(OS_BSD) |
| 430 | /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore | 632 | /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore |
| @@ -450,8 +652,84 @@ modbus_t* modbus_new_tcp(const char *ip, int port) | @@ -450,8 +652,84 @@ modbus_t* modbus_new_tcp(const char *ip, int port) | ||
| 450 | ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); | 652 | ctx->backend_data = (modbus_tcp_t *) malloc(sizeof(modbus_tcp_t)); |
| 451 | ctx_tcp = (modbus_tcp_t *)ctx->backend_data; | 653 | ctx_tcp = (modbus_tcp_t *)ctx->backend_data; |
| 452 | 654 | ||
| 453 | - strncpy(ctx_tcp->ip, ip, sizeof(char)*16); | 655 | + dest_size = sizeof(char) * 16; |
| 656 | + ret_size = strlcpy(ctx_tcp->ip, ip, dest_size); | ||
| 657 | + if (ret_size == 0) { | ||
| 658 | + fprintf(stderr, "The IP string is empty\n"); | ||
| 659 | + modbus_free(ctx); | ||
| 660 | + errno = EINVAL; | ||
| 661 | + return NULL; | ||
| 662 | + } | ||
| 663 | + | ||
| 664 | + if (ret_size >= dest_size) { | ||
| 665 | + fprintf(stderr, "The IP string has been truncated\n"); | ||
| 666 | + modbus_free(ctx); | ||
| 667 | + errno = EINVAL; | ||
| 668 | + return NULL; | ||
| 669 | + } | ||
| 670 | + | ||
| 454 | ctx_tcp->port = port; | 671 | ctx_tcp->port = port; |
| 455 | 672 | ||
| 456 | return ctx; | 673 | return ctx; |
| 457 | } | 674 | } |
| 675 | + | ||
| 676 | +/* Allocates and initializes the modbus_t structure for TCP in a protocol | ||
| 677 | + indepedent fashin, i.e. IPv4/IPv6 agnostic. | ||
| 678 | + | ||
| 679 | + - node: host name or IP address of the host to connect to, eg. '192.168.0.5' | ||
| 680 | + or 'server.com'. | ||
| 681 | + - service: service name/port number to connect to. Use NULL for the default | ||
| 682 | + port, 502/TCP. | ||
| 683 | +*/ | ||
| 684 | +modbus_t* modbus_new_tcp_pi(const char *node, const char *service) | ||
| 685 | +{ | ||
| 686 | + modbus_t *ctx; | ||
| 687 | + modbus_tcp_pi_t *ctx_tcp_pi; | ||
| 688 | + size_t dest_size; | ||
| 689 | + size_t ret_size; | ||
| 690 | + | ||
| 691 | + ctx = (modbus_t *) malloc(sizeof(modbus_t)); | ||
| 692 | + _modbus_init_common(ctx); | ||
| 693 | + | ||
| 694 | + /* Could be changed after to reach a remote serial Modbus device */ | ||
| 695 | + ctx->slave = MODBUS_TCP_SLAVE; | ||
| 696 | + | ||
| 697 | + ctx->backend = &(_modbus_tcp_pi_backend); | ||
| 698 | + | ||
| 699 | + ctx->backend_data = (modbus_tcp_pi_t *) malloc(sizeof(modbus_tcp_pi_t)); | ||
| 700 | + ctx_tcp_pi = (modbus_tcp_pi_t *)ctx->backend_data; | ||
| 701 | + | ||
| 702 | + dest_size = sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH; | ||
| 703 | + ret_size = strlcpy(ctx_tcp_pi->node, node, dest_size); | ||
| 704 | + if (ret_size == 0) { | ||
| 705 | + fprintf(stderr, "The node string is empty\n"); | ||
| 706 | + modbus_free(ctx); | ||
| 707 | + errno = EINVAL; | ||
| 708 | + return NULL; | ||
| 709 | + } | ||
| 710 | + | ||
| 711 | + if (ret_size >= dest_size) { | ||
| 712 | + fprintf(stderr, "The node string has been truncated\n"); | ||
| 713 | + modbus_free(ctx); | ||
| 714 | + errno = EINVAL; | ||
| 715 | + return NULL; | ||
| 716 | + } | ||
| 717 | + | ||
| 718 | + dest_size = sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH; | ||
| 719 | + ret_size = strlcpy(ctx_tcp_pi->service, service, dest_size); | ||
| 720 | + if (ret_size == 0) { | ||
| 721 | + fprintf(stderr, "The service string is empty\n"); | ||
| 722 | + modbus_free(ctx); | ||
| 723 | + errno = EINVAL; | ||
| 724 | + return NULL; | ||
| 725 | + } | ||
| 726 | + | ||
| 727 | + if (ret_size >= dest_size) { | ||
| 728 | + fprintf(stderr, "The service string has been truncated\n"); | ||
| 729 | + modbus_free(ctx); | ||
| 730 | + errno = EINVAL; | ||
| 731 | + return NULL; | ||
| 732 | + } | ||
| 733 | + | ||
| 734 | + return ctx; | ||
| 735 | +} |
src/modbus-tcp.h
| @@ -41,4 +41,8 @@ modbus_t* modbus_new_tcp(const char *ip_address, int port); | @@ -41,4 +41,8 @@ modbus_t* modbus_new_tcp(const char *ip_address, int port); | ||
| 41 | int modbus_tcp_listen(modbus_t *ctx, int nb_connection); | 41 | int modbus_tcp_listen(modbus_t *ctx, int nb_connection); |
| 42 | int modbus_tcp_accept(modbus_t *ctx, int *socket); | 42 | int modbus_tcp_accept(modbus_t *ctx, int *socket); |
| 43 | 43 | ||
| 44 | +modbus_t* modbus_new_tcp_pi(const char *node, const char *service); | ||
| 45 | +int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection); | ||
| 46 | +int modbus_tcp_pi_accept(modbus_t *ctx, int *socket); | ||
| 47 | + | ||
| 44 | #endif /* _MODBUS_TCP_H_ */ | 48 | #endif /* _MODBUS_TCP_H_ */ |
tests/unit-test-client.c
| @@ -26,6 +26,7 @@ | @@ -26,6 +26,7 @@ | ||
| 26 | 26 | ||
| 27 | enum { | 27 | enum { |
| 28 | TCP, | 28 | TCP, |
| 29 | + TCP_PI, | ||
| 29 | RTU | 30 | RTU |
| 30 | }; | 31 | }; |
| 31 | 32 | ||
| @@ -48,10 +49,12 @@ int main(int argc, char *argv[]) | @@ -48,10 +49,12 @@ int main(int argc, char *argv[]) | ||
| 48 | if (argc > 1) { | 49 | if (argc > 1) { |
| 49 | if (strcmp(argv[1], "tcp") == 0) { | 50 | if (strcmp(argv[1], "tcp") == 0) { |
| 50 | use_backend = TCP; | 51 | use_backend = TCP; |
| 52 | + if (strcmp(argv[1], "tcppi") == 0) { | ||
| 53 | + use_backend = TCP_PI; | ||
| 51 | } else if (strcmp(argv[1], "rtu") == 0) { | 54 | } else if (strcmp(argv[1], "rtu") == 0) { |
| 52 | use_backend = RTU; | 55 | use_backend = RTU; |
| 53 | } else { | 56 | } else { |
| 54 | - printf("Usage:\n %s [tcp|rtu] - Modbus client for unit testing\n\n", argv[0]); | 57 | + printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus client for unit testing\n\n", argv[0]); |
| 55 | exit(1); | 58 | exit(1); |
| 56 | } | 59 | } |
| 57 | } else { | 60 | } else { |
| @@ -61,7 +64,9 @@ int main(int argc, char *argv[]) | @@ -61,7 +64,9 @@ int main(int argc, char *argv[]) | ||
| 61 | 64 | ||
| 62 | if (use_backend == TCP) { | 65 | if (use_backend == TCP) { |
| 63 | ctx = modbus_new_tcp("127.0.0.1", 1502); | 66 | ctx = modbus_new_tcp("127.0.0.1", 1502); |
| 64 | - } else { | 67 | + } else if (use_backend == TCP_PI) { |
| 68 | + ctx = modbus_new_tcp_pi("::1", "1502"); | ||
| 69 | + } else | ||
| 65 | ctx = modbus_new_rtu("/dev/ttyUSB1", 115200, 'N', 8, 1); | 70 | ctx = modbus_new_rtu("/dev/ttyUSB1", 115200, 'N', 8, 1); |
| 66 | } | 71 | } |
| 67 | if (ctx == NULL) { | 72 | if (ctx == NULL) { |
tests/unit-test-server.c
| @@ -26,6 +26,7 @@ | @@ -26,6 +26,7 @@ | ||
| 26 | 26 | ||
| 27 | enum { | 27 | enum { |
| 28 | TCP, | 28 | TCP, |
| 29 | + TCP_PI, | ||
| 29 | RTU | 30 | RTU |
| 30 | }; | 31 | }; |
| 31 | 32 | ||
| @@ -43,10 +44,12 @@ int main(int argc, char*argv[]) | @@ -43,10 +44,12 @@ int main(int argc, char*argv[]) | ||
| 43 | if (argc > 1) { | 44 | if (argc > 1) { |
| 44 | if (strcmp(argv[1], "tcp") == 0) { | 45 | if (strcmp(argv[1], "tcp") == 0) { |
| 45 | use_backend = TCP; | 46 | use_backend = TCP; |
| 47 | + } else if (strcmp(argv[1], "tcppi") == 0) { | ||
| 48 | + use_backend = TCP_PI; | ||
| 46 | } else if (strcmp(argv[1], "rtu") == 0) { | 49 | } else if (strcmp(argv[1], "rtu") == 0) { |
| 47 | use_backend = RTU; | 50 | use_backend = RTU; |
| 48 | } else { | 51 | } else { |
| 49 | - printf("Usage:\n %s [tcp|rtu] - Modbus server for unit testing\n\n", argv[0]); | 52 | + printf("Usage:\n %s [tcp|tcppi|rtu] - Modbus server for unit testing\n\n", argv[0]); |
| 50 | return -1; | 53 | return -1; |
| 51 | } | 54 | } |
| 52 | } else { | 55 | } else { |
| @@ -57,6 +60,9 @@ int main(int argc, char*argv[]) | @@ -57,6 +60,9 @@ int main(int argc, char*argv[]) | ||
| 57 | if (use_backend == TCP) { | 60 | if (use_backend == TCP) { |
| 58 | ctx = modbus_new_tcp("127.0.0.1", 1502); | 61 | ctx = modbus_new_tcp("127.0.0.1", 1502); |
| 59 | query = malloc(MODBUS_TCP_MAX_ADU_LENGTH); | 62 | query = malloc(MODBUS_TCP_MAX_ADU_LENGTH); |
| 63 | + } else if (use_backend == TCP_PI) { | ||
| 64 | + ctx = modbus_new_tcp_pi("::0", "1502"); | ||
| 65 | + query = malloc(MODBUS_TCP_MAX_ADU_LENGTH); | ||
| 60 | } else { | 66 | } else { |
| 61 | ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); | 67 | ctx = modbus_new_rtu("/dev/ttyUSB0", 115200, 'N', 8, 1); |
| 62 | modbus_set_slave(ctx, SERVER_ID); | 68 | modbus_set_slave(ctx, SERVER_ID); |
| @@ -96,6 +102,9 @@ int main(int argc, char*argv[]) | @@ -96,6 +102,9 @@ int main(int argc, char*argv[]) | ||
| 96 | if (use_backend == TCP) { | 102 | if (use_backend == TCP) { |
| 97 | socket = modbus_tcp_listen(ctx, 1); | 103 | socket = modbus_tcp_listen(ctx, 1); |
| 98 | modbus_tcp_accept(ctx, &socket); | 104 | modbus_tcp_accept(ctx, &socket); |
| 105 | + } else if (use_backend == TCP_PI) { | ||
| 106 | + socket = modbus_tcp_pi_listen(ctx, 1); | ||
| 107 | + modbus_tcp_pi_accept(ctx, &socket); | ||
| 99 | } else { | 108 | } else { |
| 100 | rc = modbus_connect(ctx); | 109 | rc = modbus_connect(ctx); |
| 101 | if (rc == -1) { | 110 | if (rc == -1) { |