Coverage Report

Created: 2023-03-26 06:44

/src/openssh/canohost.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD: canohost.c,v 1.76 2023/03/03 05:00:34 djm Exp $ */
2
/*
3
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5
 *                    All rights reserved
6
 * Functions for returning the canonical host name of the remote site.
7
 *
8
 * As far as I am concerned, the code I have written for this software
9
 * can be used freely for any purpose.  Any derived versions of this
10
 * software must be clearly marked as such, and if the derived work is
11
 * incompatible with the protocol description in the RFC file, it must be
12
 * called by a name other than "ssh" or "Secure Shell".
13
 */
14
15
#include "includes.h"
16
17
#include <sys/types.h>
18
#include <sys/socket.h>
19
#include <sys/un.h>
20
21
#include <netinet/in.h>
22
#include <arpa/inet.h>
23
24
#include <errno.h>
25
#include <netdb.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <stdarg.h>
30
#include <unistd.h>
31
32
#include "xmalloc.h"
33
#include "packet.h"
34
#include "log.h"
35
#include "canohost.h"
36
#include "misc.h"
37
38
void
39
ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
40
0
{
41
0
  struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)addr;
42
0
  struct sockaddr_in *a4 = (struct sockaddr_in *)addr;
43
0
  struct in_addr inaddr;
44
0
  u_int16_t port;
45
46
0
  if (addr->ss_family != AF_INET6 ||
47
0
      !IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr))
48
0
    return;
49
50
0
  debug3("Normalising mapped IPv4 in IPv6 address");
51
52
0
  memcpy(&inaddr, ((char *)&a6->sin6_addr) + 12, sizeof(inaddr));
53
0
  port = a6->sin6_port;
54
55
0
  memset(a4, 0, sizeof(*a4));
56
57
0
  a4->sin_family = AF_INET;
58
0
  *len = sizeof(*a4);
59
0
  memcpy(&a4->sin_addr, &inaddr, sizeof(inaddr));
60
0
  a4->sin_port = port;
61
0
}
62
63
/*
64
 * Returns the local/remote IP-address/hostname of socket as a string.
65
 * The returned string must be freed.
66
 */
67
static char *
68
get_socket_address(int sock, int remote, int flags)
69
0
{
70
0
  struct sockaddr_storage addr;
71
0
  socklen_t addrlen;
72
0
  char ntop[NI_MAXHOST];
73
0
  int r;
74
75
0
  if (sock < 0)
76
0
    return NULL;
77
78
  /* Get IP address of client. */
79
0
  addrlen = sizeof(addr);
80
0
  memset(&addr, 0, sizeof(addr));
81
82
0
  if (remote) {
83
0
    if (getpeername(sock, (struct sockaddr *)&addr, &addrlen) != 0)
84
0
      return NULL;
85
0
  } else {
86
0
    if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) != 0)
87
0
      return NULL;
88
0
  }
89
90
  /* Work around Linux IPv6 weirdness */
91
0
  if (addr.ss_family == AF_INET6) {
92
0
    addrlen = sizeof(struct sockaddr_in6);
93
0
    ipv64_normalise_mapped(&addr, &addrlen);
94
0
  }
95
96
0
  switch (addr.ss_family) {
97
0
  case AF_INET:
98
0
  case AF_INET6:
99
    /* Get the address in ascii. */
100
0
    if ((r = getnameinfo((struct sockaddr *)&addr, addrlen, ntop,
101
0
        sizeof(ntop), NULL, 0, flags)) != 0) {
102
0
      error_f("getnameinfo %d failed: %s",
103
0
          flags, ssh_gai_strerror(r));
104
0
      return NULL;
105
0
    }
106
0
    return xstrdup(ntop);
107
0
  case AF_UNIX:
108
    /* Get the Unix domain socket path. */
109
0
    return xstrdup(((struct sockaddr_un *)&addr)->sun_path);
110
0
  default:
111
    /* We can't look up remote Unix domain sockets. */
112
0
    return NULL;
113
0
  }
114
0
}
115
116
char *
117
get_peer_ipaddr(int sock)
118
0
{
119
0
  char *p;
120
121
0
  if ((p = get_socket_address(sock, 1, NI_NUMERICHOST)) != NULL)
122
0
    return p;
123
0
  return xstrdup("UNKNOWN");
124
0
}
125
126
char *
127
get_local_ipaddr(int sock)
128
0
{
129
0
  char *p;
130
131
0
  if ((p = get_socket_address(sock, 0, NI_NUMERICHOST)) != NULL)
132
0
    return p;
133
0
  return xstrdup("UNKNOWN");
134
0
}
135
136
char *
137
get_local_name(int fd)
138
0
{
139
0
  char *host, myname[NI_MAXHOST];
140
141
  /* Assume we were passed a socket */
142
0
  if ((host = get_socket_address(fd, 0, NI_NAMEREQD)) != NULL)
143
0
    return host;
144
145
  /* Handle the case where we were passed a pipe */
146
0
  if (gethostname(myname, sizeof(myname)) == -1) {
147
0
    verbose_f("gethostname: %s", strerror(errno));
148
0
    host = xstrdup("UNKNOWN");
149
0
  } else {
150
0
    host = xstrdup(myname);
151
0
  }
152
153
0
  return host;
154
0
}
155
156
/* Returns the local/remote port for the socket. */
157
158
static int
159
get_sock_port(int sock, int local)
160
0
{
161
0
  struct sockaddr_storage from;
162
0
  socklen_t fromlen;
163
0
  char strport[NI_MAXSERV];
164
0
  int r;
165
166
  /* Get IP address of client. */
167
0
  fromlen = sizeof(from);
168
0
  memset(&from, 0, sizeof(from));
169
0
  if (local) {
170
0
    if (getsockname(sock, (struct sockaddr *)&from, &fromlen) == -1) {
171
0
      error("getsockname failed: %.100s", strerror(errno));
172
0
      return 0;
173
0
    }
174
0
  } else {
175
0
    if (getpeername(sock, (struct sockaddr *)&from, &fromlen) == -1) {
176
0
      debug("getpeername failed: %.100s", strerror(errno));
177
0
      return -1;
178
0
    }
179
0
  }
180
181
  /* Work around Linux IPv6 weirdness */
182
0
  if (from.ss_family == AF_INET6)
183
0
    fromlen = sizeof(struct sockaddr_in6);
184
185
  /* Non-inet sockets don't have a port number. */
186
0
  if (from.ss_family != AF_INET && from.ss_family != AF_INET6)
187
0
    return 0;
188
189
  /* Return port number. */
190
0
  if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
191
0
      strport, sizeof(strport), NI_NUMERICSERV)) != 0)
192
0
    fatal_f("getnameinfo NI_NUMERICSERV failed: %s",
193
0
        ssh_gai_strerror(r));
194
0
  return atoi(strport);
195
0
}
196
197
int
198
get_peer_port(int sock)
199
0
{
200
0
  return get_sock_port(sock, 0);
201
0
}
202
203
int
204
get_local_port(int sock)
205
0
{
206
0
  return get_sock_port(sock, 1);
207
0
}