| /* |
| * |
| * Copyright 2016 gRPC authors. |
| * |
| * Licensed 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 "src/core/ext/filters/client_channel/parse_address.h" |
| #include "src/core/lib/iomgr/sockaddr.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #ifdef GRPC_HAVE_UNIX_SOCKET |
| #include <sys/un.h> |
| #endif |
| |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/host_port.h> |
| #include <grpc/support/log.h> |
| #include <grpc/support/string_util.h> |
| #include "src/core/lib/support/string.h" |
| |
| #ifdef GRPC_HAVE_UNIX_SOCKET |
| |
| bool grpc_parse_unix(const grpc_uri *uri, |
| grpc_resolved_address *resolved_addr) { |
| if (strcmp("unix", uri->scheme) != 0) { |
| gpr_log(GPR_ERROR, "Expected 'unix' scheme, got '%s'", uri->scheme); |
| return false; |
| } |
| struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr; |
| const size_t maxlen = sizeof(un->sun_path); |
| const size_t path_len = strnlen(uri->path, maxlen); |
| if (path_len == maxlen) return false; |
| un->sun_family = AF_UNIX; |
| strcpy(un->sun_path, uri->path); |
| resolved_addr->len = sizeof(*un); |
| return true; |
| } |
| |
| #else /* GRPC_HAVE_UNIX_SOCKET */ |
| |
| bool grpc_parse_unix(const grpc_uri *uri, |
| grpc_resolved_address *resolved_addr) { |
| abort(); |
| } |
| |
| #endif /* GRPC_HAVE_UNIX_SOCKET */ |
| |
| bool grpc_parse_ipv4_hostport(const char *hostport, grpc_resolved_address *addr, |
| bool log_errors) { |
| bool success = false; |
| // Split host and port. |
| char *host; |
| char *port; |
| if (!gpr_split_host_port(hostport, &host, &port)) return false; |
| // Parse IP address. |
| memset(addr, 0, sizeof(*addr)); |
| addr->len = sizeof(struct sockaddr_in); |
| struct sockaddr_in *in = (struct sockaddr_in *)addr->addr; |
| in->sin_family = AF_INET; |
| if (inet_pton(AF_INET, host, &in->sin_addr) == 0) { |
| if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host); |
| goto done; |
| } |
| // Parse port. |
| if (port == NULL) { |
| if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv4 scheme"); |
| goto done; |
| } |
| int port_num; |
| if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) { |
| if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port); |
| goto done; |
| } |
| in->sin_port = htons((uint16_t)port_num); |
| success = true; |
| done: |
| gpr_free(host); |
| gpr_free(port); |
| return success; |
| } |
| |
| bool grpc_parse_ipv4(const grpc_uri *uri, |
| grpc_resolved_address *resolved_addr) { |
| if (strcmp("ipv4", uri->scheme) != 0) { |
| gpr_log(GPR_ERROR, "Expected 'ipv4' scheme, got '%s'", uri->scheme); |
| return false; |
| } |
| const char *host_port = uri->path; |
| if (*host_port == '/') ++host_port; |
| return grpc_parse_ipv4_hostport(host_port, resolved_addr, |
| true /* log_errors */); |
| } |
| |
| bool grpc_parse_ipv6_hostport(const char *hostport, grpc_resolved_address *addr, |
| bool log_errors) { |
| bool success = false; |
| // Split host and port. |
| char *host; |
| char *port; |
| if (!gpr_split_host_port(hostport, &host, &port)) return false; |
| // Parse IP address. |
| memset(addr, 0, sizeof(*addr)); |
| addr->len = sizeof(struct sockaddr_in6); |
| struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr->addr; |
| in6->sin6_family = AF_INET6; |
| // Handle the RFC6874 syntax for IPv6 zone identifiers. |
| char *host_end = (char *)gpr_memrchr(host, '%', strlen(host)); |
| if (host_end != NULL) { |
| GPR_ASSERT(host_end >= host); |
| char host_without_scope[INET6_ADDRSTRLEN]; |
| size_t host_without_scope_len = (size_t)(host_end - host); |
| uint32_t sin6_scope_id = 0; |
| strncpy(host_without_scope, host, host_without_scope_len); |
| host_without_scope[host_without_scope_len] = '\0'; |
| if (inet_pton(AF_INET6, host_without_scope, &in6->sin6_addr) == 0) { |
| gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope); |
| goto done; |
| } |
| if (gpr_parse_bytes_to_uint32(host_end + 1, |
| strlen(host) - host_without_scope_len - 1, |
| &sin6_scope_id) == 0) { |
| gpr_log(GPR_ERROR, "invalid ipv6 scope id: '%s'", host_end + 1); |
| goto done; |
| } |
| // Handle "sin6_scope_id" being type "u_long". See grpc issue #10027. |
| in6->sin6_scope_id = sin6_scope_id; |
| } else { |
| if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) { |
| gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); |
| goto done; |
| } |
| } |
| // Parse port. |
| if (port == NULL) { |
| if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv6 scheme"); |
| goto done; |
| } |
| int port_num; |
| if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) { |
| if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port); |
| goto done; |
| } |
| in6->sin6_port = htons((uint16_t)port_num); |
| success = true; |
| done: |
| gpr_free(host); |
| gpr_free(port); |
| return success; |
| } |
| |
| bool grpc_parse_ipv6(const grpc_uri *uri, |
| grpc_resolved_address *resolved_addr) { |
| if (strcmp("ipv6", uri->scheme) != 0) { |
| gpr_log(GPR_ERROR, "Expected 'ipv6' scheme, got '%s'", uri->scheme); |
| return false; |
| } |
| const char *host_port = uri->path; |
| if (*host_port == '/') ++host_port; |
| return grpc_parse_ipv6_hostport(host_port, resolved_addr, |
| true /* log_errors */); |
| } |
| |
| bool grpc_parse_uri(const grpc_uri *uri, grpc_resolved_address *resolved_addr) { |
| if (strcmp("unix", uri->scheme) == 0) { |
| return grpc_parse_unix(uri, resolved_addr); |
| } else if (strcmp("ipv4", uri->scheme) == 0) { |
| return grpc_parse_ipv4(uri, resolved_addr); |
| } else if (strcmp("ipv6", uri->scheme) == 0) { |
| return grpc_parse_ipv6(uri, resolved_addr); |
| } |
| gpr_log(GPR_ERROR, "Can't parse scheme '%s'", uri->scheme); |
| return false; |
| } |