| /**************************************************************************** |
| * apps/netutils/ftpc/ftpc_connect.c |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include "ftpc_config.h" |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <arpa/inet.h> |
| |
| #include "netutils/ftpc.h" |
| |
| #include "ftpc_internal.h" |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: ftpc_connect |
| * |
| * Description: |
| * Create a session handle and connect to the server. |
| * |
| ****************************************************************************/ |
| |
| SESSION ftpc_connect(FAR union ftpc_sockaddr_u *server) |
| { |
| FAR struct ftpc_session_s *session; |
| int ret; |
| |
| /* Allocate a session structure */ |
| |
| session = (struct ftpc_session_s *)zalloc(sizeof(struct ftpc_session_s)); |
| if (!session) |
| { |
| nerr("ERROR: Failed to allocate a session\n"); |
| errno = ENOMEM; |
| goto errout; |
| } |
| |
| /* Initialize the session structure with all non-zero and variable values */ |
| |
| memcpy(&session->server, server, sizeof(union ftpc_sockaddr_u)); |
| |
| session->flags &= ~FTPC_FLAGS_CLEAR; |
| session->flags |= FTPC_FLAGS_SET; |
| session->replytimeo = CONFIG_FTP_DEFTIMEO * CLOCKS_PER_SEC; |
| session->conntimeo = CONFIG_FTP_DEFTIMEO * CLOCKS_PER_SEC; |
| session->pid = getpid(); |
| |
| session->cmd.sd = -1; |
| session->data.sd = -1; |
| session->dacceptor.sd = -1; |
| |
| /* Use the default port if the user specified port number zero */ |
| |
| #ifdef CONFIG_NET_IPv6 |
| if (session->server.sa.sa_family == AF_INET6) |
| { |
| if (!session->server.in6.sin6_port) |
| { |
| session->server.in6.sin6_port = HTONS(CONFIG_FTP_DEFPORT); |
| } |
| } |
| #endif |
| |
| #ifdef CONFIG_NET_IPv4 |
| if (session->server.sa.sa_family == AF_INET) |
| { |
| if (!session->server.in4.sin_port) |
| { |
| session->server.in4.sin_port = HTONS(CONFIG_FTP_DEFPORT); |
| } |
| } |
| #endif |
| |
| /* Get the local home directory, i.e., the value of the PWD environment |
| * variable at the time of the connection. We keep a local copy so that |
| * we can change the current working directory without effecting any other |
| * logic that may be in same context. |
| */ |
| |
| session->homeldir = strdup(ftpc_lpwd()); |
| |
| /* And (Re-)connect to the server */ |
| |
| ret = ftpc_reconnect(session); |
| if (ret != OK) |
| { |
| nerr("ERROR: ftpc_reconnect() failed: %d\n", errno); |
| goto errout_with_alloc; |
| } |
| |
| return (SESSION)session; |
| |
| errout_with_alloc: |
| if (session->homeldir != NULL) |
| { |
| free(session->homeldir); |
| } |
| |
| free(session); |
| errout: |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: ftpc_reconnect |
| * |
| * Description: |
| * re-connect to the server either initially, or after loss of connection. |
| * |
| ****************************************************************************/ |
| |
| int ftpc_reconnect(FAR struct ftpc_session_s *session) |
| { |
| #ifdef CONFIG_DEBUG_NET_ERROR |
| char buffer[48]; |
| #endif |
| int ret; |
| |
| /* Re-initialize the session structure */ |
| |
| session->replytimeo = CONFIG_FTP_DEFTIMEO * CLOCKS_PER_SEC; |
| session->conntimeo = CONFIG_FTP_DEFTIMEO * CLOCKS_PER_SEC; |
| session->xfrmode = FTPC_XFRMODE_UNKNOWN; |
| |
| /* Set up a timer to prevent hangs */ |
| |
| ret = wd_start(&session->wdog, session->conntimeo, |
| ftpc_timeout, (wdparm_t)session); |
| if (ret != OK) |
| { |
| nerr("ERROR: wd_start() failed\n"); |
| goto errout; |
| } |
| |
| /* Initialize a socket */ |
| |
| ret = ftpc_sockinit(&session->cmd, session->server.sa.sa_family); |
| if (ret != OK) |
| { |
| nerr("ERROR: ftpc_sockinit() failed: %d\n", errno); |
| goto errout; |
| } |
| |
| /* Connect the socket to the server */ |
| |
| #ifdef CONFIG_DEBUG_NET_ERROR |
| #ifdef CONFIG_NET_IPv6 |
| if (session->server.sa.sa_family == AF_INET6) |
| { |
| if (inet_ntop(AF_INET6, &session->server.in6.sin6_addr, buffer, 48)) |
| { |
| ninfo("Connecting to server address %s:%d\n", buffer, |
| ntohs(session->server.in6.sin6_port)); |
| } |
| } |
| #endif /* CONFIG_NET_IPv6 */ |
| |
| #ifdef CONFIG_NET_IPv4 |
| if (session->server.sa.sa_family == AF_INET) |
| { |
| if (inet_ntop(AF_INET, &session->server.in4.sin_addr, buffer, 48)) |
| { |
| ninfo("Connecting to server address %s:%d\n", buffer, |
| ntohs(session->server.in4.sin_port)); |
| } |
| } |
| #endif /* CONFIG_NET_IPv4 */ |
| #endif /* CONFIG_DEBUG_NET_ERROR */ |
| |
| ret = ftpc_sockconnect(&session->cmd, |
| (FAR struct sockaddr *)&session->server); |
| if (ret != OK) |
| { |
| nerr("ERROR: ftpc_sockconnect() failed: %d\n", errno); |
| goto errout_with_socket; |
| } |
| |
| /* Read startup message from server */ |
| |
| fptc_getreply(session); |
| |
| /* Check for "120 Service ready in nnn minutes" */ |
| |
| if (session->code == 120) |
| { |
| fptc_getreply(session); |
| } |
| |
| wd_cancel(&session->wdog); |
| |
| if (!ftpc_sockconnected(&session->cmd)) |
| { |
| ftpc_reset(session); |
| goto errout; |
| } |
| |
| /* Check for "220 Service ready for new user" */ |
| |
| if (session->code == 220) |
| { |
| FTPC_SET_CONNECTED(session); |
| } |
| |
| if (!FTPC_IS_CONNECTED(session)) |
| { |
| goto errout_with_socket; |
| } |
| |
| #ifdef CONFIG_DEBUG_NET_ERROR |
| ninfo("Connected\n"); |
| #ifdef CONFIG_NET_IPv6 |
| if (session->server.sa.sa_family == AF_INET6) |
| { |
| if (inet_ntop(AF_INET6, &session->server.in6.sin6_addr, buffer, 48)) |
| { |
| ninfo(" Remote address: %s:%d\n", buffer, |
| ntohs(session->server.in6.sin6_port)); |
| } |
| |
| if (inet_ntop(AF_INET6, &session->cmd.laddr.in6.sin6_addr, buffer, 48)) |
| { |
| ninfo(" Local address: %s:%d\n", buffer, |
| ntohs(session->cmd.laddr.in6.sin6_port)); |
| } |
| } |
| #endif /* CONFIG_NET_IPv6 */ |
| |
| #ifdef CONFIG_NET_IPv4 |
| if (session->server.sa.sa_family == AF_INET) |
| { |
| if (inet_ntop(AF_INET, &session->server.in4.sin_addr, buffer, 48)) |
| { |
| ninfo(" Remote address: %s:%d\n", buffer, |
| ntohs(session->server.in4.sin_port)); |
| } |
| |
| if (inet_ntop(AF_INET, &session->cmd.laddr.in4.sin_addr, buffer, 48)) |
| { |
| ninfo(" Local address: %s:%d\n", buffer, |
| ntohs(session->cmd.laddr.in4.sin_port)); |
| } |
| } |
| #endif /* CONFIG_NET_IPv4 */ |
| #endif /* CONFIG_DEBUG_NET_ERROR */ |
| |
| return OK; |
| |
| errout_with_socket: |
| ftpc_sockclose(&session->cmd); |
| errout: |
| if (WDOG_ISACTIVE(&session->wdog)) |
| { |
| wd_cancel(&session->wdog); |
| } |
| |
| return ERROR; |
| } |