diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index b52f476e..06ced8cc 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -70,6 +70,7 @@ libguacinc_HEADERS = \ guacamole/socket-constants.h \ guacamole/socket.h \ guacamole/socket-fntypes.h \ + guacamole/socket-tcp.h \ guacamole/socket-types.h \ guacamole/stream.h \ guacamole/stream-types.h \ @@ -128,6 +129,7 @@ libguac_la_SOURCES = \ socket-broadcast.c \ socket-fd.c \ socket-nest.c \ + socket-tcp.c \ socket-tee.c \ string.c \ timestamp.c \ diff --git a/src/libguac/guacamole/socket-tcp.h b/src/libguac/guacamole/socket-tcp.h new file mode 100644 index 00000000..48c0743a --- /dev/null +++ b/src/libguac/guacamole/socket-tcp.h @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef __GUAC_SOCKET_TCP_H +#define __GUAC_SOCKET_TCP_H + +#include "config.h" + +#include + +/** + * Given a hostname or IP address and port, attempt to connect to that + * system, returning an open socket if the connection succeeds, or a negative + * value if it fails. If it fails the errno variable will be set. + * + * @param hostname + * The hostname or IP address to which to attempt connections. + * + * @param port + * The TCP port to which to attempt to connect. + * + * @return + * A valid socket if the connection succeeds, or a negative integer if it + * fails. + */ +int guac_socket_tcp_connect(const char* hostname, const char* port); + +#endif // __GUAC_SOCKET_TCP_H \ No newline at end of file diff --git a/src/libguac/socket-tcp.c b/src/libguac/socket-tcp.c new file mode 100644 index 00000000..7e41d021 --- /dev/null +++ b/src/libguac/socket-tcp.c @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" +#include "guacamole/error.h" +#include "guacamole/socket.h" + +#include +#include +#include +#include + +int guac_socket_tcp_connect(const char* hostname, const char* port) { + + int retval; + + int fd = EBADFD; + struct addrinfo* addresses; + struct addrinfo* current_address; + + char connected_address[1024]; + char connected_port[64]; + + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP + }; + + /* Get addresses for requested hostname and port. */ + if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) { + guac_error = GUAC_STATUS_INVALID_ARGUMENT; + guac_error_message = "Error parsing address or port."; + return retval; + } + + /* Attempt connection to each address until success */ + current_address = addresses; + while (current_address != NULL) { + + /* Resolve hostname */ + if ((retval = getnameinfo(current_address->ai_addr, + current_address->ai_addrlen, + connected_address, sizeof(connected_address), + connected_port, sizeof(connected_port), + NI_NUMERICHOST | NI_NUMERICSERV))) { + + guac_error = GUAC_STATUS_INVALID_ARGUMENT; + guac_error_message = "Error resolving host."; + continue; + } + + /* Get socket */ + fd = socket(current_address->ai_family, SOCK_STREAM, 0); + if (fd < 0) { + freeaddrinfo(addresses); + return fd; + } + + /* Connect */ + if (connect(fd, current_address->ai_addr, + current_address->ai_addrlen) == 0) { + + /* Done if successful connect */ + break; + + } + + close(fd); + current_address = current_address->ai_next; + + } + + /* Free addrinfo */ + freeaddrinfo(addresses); + + /* If unable to connect to anything, set error status. */ + if (current_address == NULL) { + guac_error = GUAC_STATUS_REFUSED; + guac_error_message = "Unable to connect to remote host."; + } + + /* Return the fd, or the error message if the socket connection failed. */ + return fd; + +} \ No newline at end of file