Coverage Report

Created: 2025-07-18 06:46

/src/openssh/canohost.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD: canohost.c,v 1.77 2023/03/31 04:42:29 dtucker 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
0
  if (sock < 0)
167
0
    return -1;
168
  /* Get IP address of client. */
169
0
  fromlen = sizeof(from);
170
0
  memset(&from, 0, sizeof(from));
171
0
  if (local) {
172
0
    if (getsockname(sock, (struct sockaddr *)&from, &fromlen) == -1) {
173
0
      error("getsockname failed: %.100s", strerror(errno));
174
0
      return 0;
175
0
    }
176
0
  } else {
177
0
    if (getpeername(sock, (struct sockaddr *)&from, &fromlen) == -1) {
178
0
      debug("getpeername failed: %.100s", strerror(errno));
179
0
      return -1;
180
0
    }
181
0
  }
182
183
  /* Work around Linux IPv6 weirdness */
184
0
  if (from.ss_family == AF_INET6)
185
0
    fromlen = sizeof(struct sockaddr_in6);
186
187
  /* Non-inet sockets don't have a port number. */
188
0
  if (from.ss_family != AF_INET && from.ss_family != AF_INET6)
189
0
    return 0;
190
191
  /* Return port number. */
192
0
  if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
193
0
      strport, sizeof(strport), NI_NUMERICSERV)) != 0)
194
0
    fatal_f("getnameinfo NI_NUMERICSERV failed: %s",
195
0
        ssh_gai_strerror(r));
196
0
  return atoi(strport);
197
0
}
198
199
int
200
get_peer_port(int sock)
201
0
{
202
0
  return get_sock_port(sock, 0);
203
0
}
204
205
int
206
get_local_port(int sock)
207
0
{
208
0
  return get_sock_port(sock, 1);
209
0
}