2 * Copyright © 2001-2013 Stéphane Raimbault <stephane.raimbault@gmail.com>
4 * SPDX-License-Identifier: LGPL-2.1+
15 #include <sys/types.h>
19 /* ws2_32.dll has getaddrinfo and freeaddrinfo on Windows XP and later.
20 * minwg32 headers check WINVER before allowing the use of these */
22 # define WINVER 0x0501
24 /* Already set in modbus-tcp.h but it seems order matters in VS2005 */
25 # include <winsock2.h>
26 # include <ws2tcpip.h>
28 # define close closesocket
30 # include <sys/socket.h>
31 # include <sys/ioctl.h>
33 #if defined(__OpenBSD__) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
35 # include <netinet/in_systm.h>
38 # include <netinet/in.h>
39 # include <netinet/ip.h>
40 # include <netinet/tcp.h>
41 # include <arpa/inet.h>
45 #if !defined(MSG_NOSIGNAL)
46 #define MSG_NOSIGNAL 0
49 #include "modbus-private.h"
51 #include "modbus-tcp.h"
52 #include "modbus-tcp-private.h"
55 static int _modbus_tcp_init_win32(void)
57 /* Initialise Windows Socket API */
60 if (WSAStartup(MAKEWORD(2, 2), &wsaData
) != 0) {
61 fprintf(stderr
, "WSAStartup() returned error code %d\n",
62 (unsigned int)GetLastError());
70 static int _modbus_set_slave(modbus_t
*ctx
, int slave
)
72 /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
73 if (slave
>= 0 && slave
<= 247) {
75 } else if (slave
== MODBUS_TCP_SLAVE
) {
76 /* The special value MODBUS_TCP_SLAVE (0xFF) can be used in TCP mode to
77 * restore the default value. */
87 /* Builds a TCP request header */
88 static int _modbus_tcp_build_request_basis(modbus_t
*ctx
, int function
,
92 modbus_tcp_t
*ctx_tcp
= ctx
->backend_data
;
94 /* Increase transaction ID */
95 if (ctx_tcp
->t_id
< UINT16_MAX
)
99 req
[0] = ctx_tcp
->t_id
>> 8;
100 req
[1] = ctx_tcp
->t_id
& 0x00ff;
102 /* Protocol Modbus */
106 /* Length will be defined later by set_req_length_tcp at offsets 4
112 req
[9] = addr
& 0x00ff;
114 req
[11] = nb
& 0x00ff;
116 return _MODBUS_TCP_PRESET_REQ_LENGTH
;
119 /* Builds a TCP response header */
120 static int _modbus_tcp_build_response_basis(sft_t
*sft
, uint8_t *rsp
)
122 /* Extract from MODBUS Messaging on TCP/IP Implementation
123 Guide V1.0b (page 23/46):
124 The transaction identifier is used to associate the future
125 response with the request. */
126 rsp
[0] = sft
->t_id
>> 8;
127 rsp
[1] = sft
->t_id
& 0x00ff;
129 /* Protocol Modbus */
133 /* Length will be set later by send_msg (4 and 5) */
135 /* The slave ID is copied from the indication */
137 rsp
[7] = sft
->function
;
139 return _MODBUS_TCP_PRESET_RSP_LENGTH
;
143 static int _modbus_tcp_prepare_response_tid(const uint8_t *req
, int *req_length
)
145 return (req
[0] << 8) + req
[1];
148 static int _modbus_tcp_send_msg_pre(uint8_t *req
, int req_length
)
150 /* Substract the header length to the message length */
151 int mbap_length
= req_length
- 6;
153 req
[4] = mbap_length
>> 8;
154 req
[5] = mbap_length
& 0x00FF;
159 static ssize_t
_modbus_tcp_send(modbus_t
*ctx
, const uint8_t *req
, int req_length
)
162 Requests not to send SIGPIPE on errors on stream oriented
163 sockets when the other end breaks the connection. The EPIPE
164 error is still returned. */
165 return send(ctx
->s
, (const char *)req
, req_length
, MSG_NOSIGNAL
);
168 static int _modbus_tcp_receive(modbus_t
*ctx
, uint8_t *req
) {
169 return _modbus_receive_msg(ctx
, req
, MSG_INDICATION
);
172 static ssize_t
_modbus_tcp_recv(modbus_t
*ctx
, uint8_t *rsp
, int rsp_length
) {
173 return recv(ctx
->s
, (char *)rsp
, rsp_length
, 0);
176 static int _modbus_tcp_check_integrity(modbus_t
*ctx
, uint8_t *msg
, const int msg_length
)
181 static int _modbus_tcp_pre_check_confirmation(modbus_t
*ctx
, const uint8_t *req
,
182 const uint8_t *rsp
, int rsp_length
)
184 /* Check transaction ID */
185 if (req
[0] != rsp
[0] || req
[1] != rsp
[1]) {
187 fprintf(stderr
, "Invalid transaction ID received 0x%X (not 0x%X)\n",
188 (rsp
[0] << 8) + rsp
[1], (req
[0] << 8) + req
[1]);
194 /* Check protocol ID */
195 if (rsp
[2] != 0x0 && rsp
[3] != 0x0) {
197 fprintf(stderr
, "Invalid protocol ID received 0x%X (not 0x0)\n",
198 (rsp
[2] << 8) + rsp
[3]);
207 static int _modbus_tcp_set_ipv4_options(int s
)
212 /* Set the TCP no delay flag */
213 /* SOL_TCP = IPPROTO_TCP */
215 rc
= setsockopt(s
, IPPROTO_TCP
, TCP_NODELAY
,
216 (const void *)&option
, sizeof(int));
221 /* If the OS does not offer SOCK_NONBLOCK, fall back to setting FIONBIO to
222 * make sockets non-blocking */
223 /* Do not care about the return value, this is optional */
225 #if !defined(SOCK_NONBLOCK) && defined(FIONBIO)
228 /* Setting FIONBIO expects an unsigned long according to MSDN */
230 ioctlsocket(s
, FIONBIO
, &loption
);
233 ioctl(s
, FIONBIO
, &option
);
239 * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
240 * necessary to workaround that problem.
242 /* Set the IP low delay option */
243 option
= IPTOS_LOWDELAY
;
244 rc
= setsockopt(s
, IPPROTO_IP
, IP_TOS
,
245 (const void *)&option
, sizeof(int));
254 static int _connect(int sockfd
, const struct sockaddr
*addr
, socklen_t addrlen
,
255 const struct timeval
*ro_tv
)
257 int rc
= connect(sockfd
, addr
, addrlen
);
262 wsaError
= WSAGetLastError();
265 if (wsaError
== WSAEWOULDBLOCK
|| wsaError
== WSAEINPROGRESS
) {
267 if (rc
== -1 && errno
== EINPROGRESS
) {
271 socklen_t optlen
= sizeof(optval
);
272 struct timeval tv
= *ro_tv
;
274 /* Wait to be available in writing */
276 FD_SET(sockfd
, &wset
);
277 rc
= select(sockfd
+ 1, NULL
, &wset
, NULL
, &tv
);
279 /* Timeout or fail */
283 /* The connection is established if SO_ERROR and optval are set to 0 */
284 rc
= getsockopt(sockfd
, SOL_SOCKET
, SO_ERROR
, (void *)&optval
, &optlen
);
285 if (rc
== 0 && optval
== 0) {
288 errno
= ECONNREFUSED
;
295 /* Establishes a modbus TCP connection with a Modbus server. */
296 static int _modbus_tcp_connect(modbus_t
*ctx
)
299 /* Specialized version of sockaddr for Internet socket address (same size) */
300 struct sockaddr_in addr
;
301 modbus_tcp_t
*ctx_tcp
= ctx
->backend_data
;
302 int flags
= SOCK_STREAM
;
305 if (_modbus_tcp_init_win32() == -1) {
311 flags
|= SOCK_CLOEXEC
;
315 flags
|= SOCK_NONBLOCK
;
318 ctx
->s
= socket(PF_INET
, flags
, 0);
323 rc
= _modbus_tcp_set_ipv4_options(ctx
->s
);
331 printf("Connecting to %s:%d\n", ctx_tcp
->ip
, ctx_tcp
->port
);
334 addr
.sin_family
= AF_INET
;
335 addr
.sin_port
= htons(ctx_tcp
->port
);
336 addr
.sin_addr
.s_addr
= inet_addr(ctx_tcp
->ip
);
337 rc
= _connect(ctx
->s
, (struct sockaddr
*)&addr
, sizeof(addr
), &ctx
->response_timeout
);
347 /* Establishes a modbus TCP PI connection with a Modbus server. */
348 static int _modbus_tcp_pi_connect(modbus_t
*ctx
)
351 struct addrinfo
*ai_list
;
352 struct addrinfo
*ai_ptr
;
353 struct addrinfo ai_hints
;
354 modbus_tcp_pi_t
*ctx_tcp_pi
= ctx
->backend_data
;
357 if (_modbus_tcp_init_win32() == -1) {
362 memset(&ai_hints
, 0, sizeof(ai_hints
));
364 ai_hints
.ai_flags
|= AI_ADDRCONFIG
;
366 ai_hints
.ai_family
= AF_UNSPEC
;
367 ai_hints
.ai_socktype
= SOCK_STREAM
;
368 ai_hints
.ai_addr
= NULL
;
369 ai_hints
.ai_canonname
= NULL
;
370 ai_hints
.ai_next
= NULL
;
373 rc
= getaddrinfo(ctx_tcp_pi
->node
, ctx_tcp_pi
->service
,
374 &ai_hints
, &ai_list
);
377 fprintf(stderr
, "Error returned by getaddrinfo: %s\n", gai_strerror(rc
));
379 errno
= ECONNREFUSED
;
383 for (ai_ptr
= ai_list
; ai_ptr
!= NULL
; ai_ptr
= ai_ptr
->ai_next
) {
384 int flags
= ai_ptr
->ai_socktype
;
388 flags
|= SOCK_CLOEXEC
;
392 flags
|= SOCK_NONBLOCK
;
395 s
= socket(ai_ptr
->ai_family
, flags
, ai_ptr
->ai_protocol
);
399 if (ai_ptr
->ai_family
== AF_INET
)
400 _modbus_tcp_set_ipv4_options(s
);
403 printf("Connecting to [%s]:%s\n", ctx_tcp_pi
->node
, ctx_tcp_pi
->service
);
406 rc
= _connect(s
, ai_ptr
->ai_addr
, ai_ptr
->ai_addrlen
, &ctx
->response_timeout
);
416 freeaddrinfo(ai_list
);
425 /* Closes the network connection and socket in TCP mode */
426 static void _modbus_tcp_close(modbus_t
*ctx
)
429 shutdown(ctx
->s
, SHUT_RDWR
);
435 static int _modbus_tcp_flush(modbus_t
*ctx
)
441 /* Extract the garbage from the socket */
442 char devnull
[MODBUS_TCP_MAX_ADU_LENGTH
];
444 rc
= recv(ctx
->s
, devnull
, MODBUS_TCP_MAX_ADU_LENGTH
, MSG_DONTWAIT
);
446 /* On Win32, it's a bit more complicated to not wait */
453 FD_SET(ctx
->s
, &rset
);
454 rc
= select(ctx
->s
+1, &rset
, NULL
, NULL
, &tv
);
460 /* There is data to flush */
461 rc
= recv(ctx
->s
, devnull
, MODBUS_TCP_MAX_ADU_LENGTH
, 0);
467 } while (rc
== MODBUS_TCP_MAX_ADU_LENGTH
);
472 /* Listens for any request from one or many modbus masters in TCP */
473 int modbus_tcp_listen(modbus_t
*ctx
, int nb_connection
)
477 struct sockaddr_in addr
;
478 modbus_tcp_t
*ctx_tcp
;
485 ctx_tcp
= ctx
->backend_data
;
488 if (_modbus_tcp_init_win32() == -1) {
493 new_s
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
499 if (setsockopt(new_s
, SOL_SOCKET
, SO_REUSEADDR
,
500 (char *)&enable
, sizeof(enable
)) == -1) {
505 memset(&addr
, 0, sizeof(addr
));
506 addr
.sin_family
= AF_INET
;
507 /* If the modbus port is < to 1024, we need the setuid root. */
508 addr
.sin_port
= htons(ctx_tcp
->port
);
509 if (ctx_tcp
->ip
[0] == '0') {
510 /* Listen any addresses */
511 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
513 /* Listen only specified IP address */
514 addr
.sin_addr
.s_addr
= inet_addr(ctx_tcp
->ip
);
516 if (bind(new_s
, (struct sockaddr
*)&addr
, sizeof(addr
)) == -1) {
521 if (listen(new_s
, nb_connection
) == -1) {
529 int modbus_tcp_pi_listen(modbus_t
*ctx
, int nb_connection
)
532 struct addrinfo
*ai_list
;
533 struct addrinfo
*ai_ptr
;
534 struct addrinfo ai_hints
;
538 modbus_tcp_pi_t
*ctx_tcp_pi
;
545 ctx_tcp_pi
= ctx
->backend_data
;
548 if (_modbus_tcp_init_win32() == -1) {
553 if (ctx_tcp_pi
->node
[0] == 0) {
554 node
= NULL
; /* == any */
556 node
= ctx_tcp_pi
->node
;
559 if (ctx_tcp_pi
->service
[0] == 0) {
562 service
= ctx_tcp_pi
->service
;
565 memset(&ai_hints
, 0, sizeof (ai_hints
));
566 /* If node is not NULL, than the AI_PASSIVE flag is ignored. */
567 ai_hints
.ai_flags
|= AI_PASSIVE
;
569 ai_hints
.ai_flags
|= AI_ADDRCONFIG
;
571 ai_hints
.ai_family
= AF_UNSPEC
;
572 ai_hints
.ai_socktype
= SOCK_STREAM
;
573 ai_hints
.ai_addr
= NULL
;
574 ai_hints
.ai_canonname
= NULL
;
575 ai_hints
.ai_next
= NULL
;
578 rc
= getaddrinfo(node
, service
, &ai_hints
, &ai_list
);
581 fprintf(stderr
, "Error returned by getaddrinfo: %s\n", gai_strerror(rc
));
583 errno
= ECONNREFUSED
;
588 for (ai_ptr
= ai_list
; ai_ptr
!= NULL
; ai_ptr
= ai_ptr
->ai_next
) {
591 s
= socket(ai_ptr
->ai_family
, ai_ptr
->ai_socktype
,
592 ai_ptr
->ai_protocol
);
600 rc
= setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
,
601 (void *)&enable
, sizeof (enable
));
605 perror("setsockopt");
611 rc
= bind(s
, ai_ptr
->ai_addr
, ai_ptr
->ai_addrlen
);
620 rc
= listen(s
, nb_connection
);
632 freeaddrinfo(ai_list
);
641 int modbus_tcp_accept(modbus_t
*ctx
, int *s
)
643 struct sockaddr_in addr
;
651 addrlen
= sizeof(addr
);
653 /* Inherit socket flags and use accept4 call */
654 ctx
->s
= accept4(*s
, (struct sockaddr
*)&addr
, &addrlen
, SOCK_CLOEXEC
);
656 ctx
->s
= accept(*s
, (struct sockaddr
*)&addr
, &addrlen
);
666 printf("The client connection from %s is accepted\n",
667 inet_ntoa(addr
.sin_addr
));
673 int modbus_tcp_pi_accept(modbus_t
*ctx
, int *s
)
675 struct sockaddr_storage addr
;
683 addrlen
= sizeof(addr
);
685 /* Inherit socket flags and use accept4 call */
686 ctx
->s
= accept4(*s
, (struct sockaddr
*)&addr
, &addrlen
, SOCK_CLOEXEC
);
688 ctx
->s
= accept(*s
, (struct sockaddr
*)&addr
, &addrlen
);
696 printf("The client connection is accepted.\n");
702 static int _modbus_tcp_select(modbus_t
*ctx
, fd_set
*rset
, struct timeval
*tv
, int length_to_read
)
705 while ((s_rc
= select(ctx
->s
+1, rset
, NULL
, NULL
, tv
)) == -1) {
706 if (errno
== EINTR
) {
708 fprintf(stderr
, "A non blocked signal was caught\n");
710 /* Necessary after an error */
712 FD_SET(ctx
->s
, rset
);
726 static void _modbus_tcp_free(modbus_t
*ctx
) {
727 free(ctx
->backend_data
);
731 const modbus_backend_t _modbus_tcp_backend
= {
732 _MODBUS_BACKEND_TYPE_TCP
,
733 _MODBUS_TCP_HEADER_LENGTH
,
734 _MODBUS_TCP_CHECKSUM_LENGTH
,
735 MODBUS_TCP_MAX_ADU_LENGTH
,
737 _modbus_tcp_build_request_basis
,
738 _modbus_tcp_build_response_basis
,
739 _modbus_tcp_prepare_response_tid
,
740 _modbus_tcp_send_msg_pre
,
744 _modbus_tcp_check_integrity
,
745 _modbus_tcp_pre_check_confirmation
,
754 const modbus_backend_t _modbus_tcp_pi_backend
= {
755 _MODBUS_BACKEND_TYPE_TCP
,
756 _MODBUS_TCP_HEADER_LENGTH
,
757 _MODBUS_TCP_CHECKSUM_LENGTH
,
758 MODBUS_TCP_MAX_ADU_LENGTH
,
760 _modbus_tcp_build_request_basis
,
761 _modbus_tcp_build_response_basis
,
762 _modbus_tcp_prepare_response_tid
,
763 _modbus_tcp_send_msg_pre
,
767 _modbus_tcp_check_integrity
,
768 _modbus_tcp_pre_check_confirmation
,
769 _modbus_tcp_pi_connect
,
776 modbus_t
* modbus_new_tcp(const char *ip
, int port
)
779 modbus_tcp_t
*ctx_tcp
;
784 /* MSG_NOSIGNAL is unsupported on *BSD so we install an ignore
785 handler for SIGPIPE. */
788 sa
.sa_handler
= SIG_IGN
;
789 if (sigaction(SIGPIPE
, &sa
, NULL
) < 0) {
790 /* The debug flag can't be set here... */
791 fprintf(stderr
, "Coud not install SIGPIPE handler.\n");
796 ctx
= (modbus_t
*)malloc(sizeof(modbus_t
));
797 _modbus_init_common(ctx
);
799 /* Could be changed after to reach a remote serial Modbus device */
800 ctx
->slave
= MODBUS_TCP_SLAVE
;
802 ctx
->backend
= &_modbus_tcp_backend
;
804 ctx
->backend_data
= (modbus_tcp_t
*)malloc(sizeof(modbus_tcp_t
));
805 ctx_tcp
= (modbus_tcp_t
*)ctx
->backend_data
;
808 dest_size
= sizeof(char) * 16;
809 ret_size
= strlcpy(ctx_tcp
->ip
, ip
, dest_size
);
811 fprintf(stderr
, "The IP string is empty\n");
817 if (ret_size
>= dest_size
) {
818 fprintf(stderr
, "The IP string has been truncated\n");
824 ctx_tcp
->ip
[0] = '0';
826 ctx_tcp
->port
= port
;
833 modbus_t
* modbus_new_tcp_pi(const char *node
, const char *service
)
836 modbus_tcp_pi_t
*ctx_tcp_pi
;
840 ctx
= (modbus_t
*)malloc(sizeof(modbus_t
));
841 _modbus_init_common(ctx
);
843 /* Could be changed after to reach a remote serial Modbus device */
844 ctx
->slave
= MODBUS_TCP_SLAVE
;
846 ctx
->backend
= &_modbus_tcp_pi_backend
;
848 ctx
->backend_data
= (modbus_tcp_pi_t
*)malloc(sizeof(modbus_tcp_pi_t
));
849 ctx_tcp_pi
= (modbus_tcp_pi_t
*)ctx
->backend_data
;
852 /* The node argument can be empty to indicate any hosts */
853 ctx_tcp_pi
->node
[0] = '0';
855 dest_size
= sizeof(char) * _MODBUS_TCP_PI_NODE_LENGTH
;
856 ret_size
= strlcpy(ctx_tcp_pi
->node
, node
, dest_size
);
858 fprintf(stderr
, "The node string is empty\n");
864 if (ret_size
>= dest_size
) {
865 fprintf(stderr
, "The node string has been truncated\n");
872 if (service
!= NULL
) {
873 dest_size
= sizeof(char) * _MODBUS_TCP_PI_SERVICE_LENGTH
;
874 ret_size
= strlcpy(ctx_tcp_pi
->service
, service
, dest_size
);
876 /* Empty service is not allowed, error catched below. */
881 fprintf(stderr
, "The service string is empty\n");
887 if (ret_size
>= dest_size
) {
888 fprintf(stderr
, "The service string has been truncated\n");
894 ctx_tcp_pi
->t_id
= 0;