Coverage Report

Created: 2026-03-21 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssh/canohost.c
Line
Count
Source
1
/* $OpenBSD: canohost.c,v 1.78 2026/02/14 00:18:34 jsg 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 "log.h"
34
#include "canohost.h"
35
#include "misc.h"
36
37
void
38
ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
39
0
{
40
0
  struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)addr;
41
0
  struct sockaddr_in *a4 = (struct sockaddr_in *)addr;
42
0
  struct in_addr inaddr;
43
0
  uint16_t port;
44
45
0
  if (addr->ss_family != AF_INET6 ||
46
0
      !IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr))
47
0
    return;
48
49
0
  debug3("Normalising mapped IPv4 in IPv6 address");
50
51
0
  memcpy(&inaddr, ((char *)&a6->sin6_addr) + 12, sizeof(inaddr));
52
0
  port = a6->sin6_port;
53
54
0
  memset(a4, 0, sizeof(*a4));
55
56
0
  a4->sin_family = AF_INET;
57
0
  *len = sizeof(*a4);
58
0
  memcpy(&a4->sin_addr, &inaddr, sizeof(inaddr));
59
0
  a4->sin_port = port;
60
0
}
61
62
/*
63
 * Returns the local/remote IP-address/hostname of socket as a string.
64
 * The returned string must be freed.
65
 */
66
static char *
67
get_socket_address(int sock, int remote, int flags)
68
0
{
69
0
  struct sockaddr_storage addr;
70
0
  socklen_t addrlen;
71
0
  char ntop[NI_MAXHOST];
72
0
  int r;
73
74
0
  if (sock < 0)
75
0
    return NULL;
76
77
  /* Get IP address of client. */
78
0
  addrlen = sizeof(addr);
79
0
  memset(&addr, 0, sizeof(addr));
80
81
0
  if (remote) {
82
0
    if (getpeername(sock, (struct sockaddr *)&addr, &addrlen) != 0)
83
0
      return NULL;
84
0
  } else {
85
0
    if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) != 0)
86
0
      return NULL;
87
0
  }
88
89
  /* Work around Linux IPv6 weirdness */
90
0
  if (addr.ss_family == AF_INET6) {
91
0
    addrlen = sizeof(struct sockaddr_in6);
92
0
    ipv64_normalise_mapped(&addr, &addrlen);
93
0
  }
94
95
0
  switch (addr.ss_family) {
96
0
  case AF_INET:
97
0
  case AF_INET6:
98
    /* Get the address in ascii. */
99
0
    if ((r = getnameinfo((struct sockaddr *)&addr, addrlen, ntop,
100
0
        sizeof(ntop), NULL, 0, flags)) != 0) {
101
0
      error_f("getnameinfo %d failed: %s",
102
0
          flags, ssh_gai_strerror(r));
103
0
      return NULL;
104
0
    }
105
0
    return xstrdup(ntop);
106
0
  case AF_UNIX:
107
    /* Get the Unix domain socket path. */
108
0
    return xstrdup(((struct sockaddr_un *)&addr)->sun_path);
109
0
  default:
110
    /* We can't look up remote Unix domain sockets. */
111
0
    return NULL;
112
0
  }
113
0
}
114
115
char *
116
get_peer_ipaddr(int sock)
117
0
{
118
0
  char *p;
119
120
0
  if ((p = get_socket_address(sock, 1, NI_NUMERICHOST)) != NULL)
121
0
    return p;
122
0
  return xstrdup("UNKNOWN");
123
0
}
124
125
char *
126
get_local_ipaddr(int sock)
127
0
{
128
0
  char *p;
129
130
0
  if ((p = get_socket_address(sock, 0, NI_NUMERICHOST)) != NULL)
131
0
    return p;
132
0
  return xstrdup("UNKNOWN");
133
0
}
134
135
char *
136
get_local_name(int fd)
137
0
{
138
0
  char *host, myname[NI_MAXHOST];
139
140
  /* Assume we were passed a socket */
141
0
  if ((host = get_socket_address(fd, 0, NI_NAMEREQD)) != NULL)
142
0
    return host;
143
144
  /* Handle the case where we were passed a pipe */
145
0
  if (gethostname(myname, sizeof(myname)) == -1) {
146
0
    verbose_f("gethostname: %s", strerror(errno));
147
0
    host = xstrdup("UNKNOWN");
148
0
  } else {
149
0
    host = xstrdup(myname);
150
0
  }
151
152
0
  return host;
153
0
}
154
155
/* Returns the local/remote port for the socket. */
156
157
static int
158
get_sock_port(int sock, int local)
159
0
{
160
0
  struct sockaddr_storage from;
161
0
  socklen_t fromlen;
162
0
  char strport[NI_MAXSERV];
163
0
  int r;
164
165
0
  if (sock < 0)
166
0
    return -1;
167
  /* Get IP address of client. */
168
0
  fromlen = sizeof(from);
169
0
  memset(&from, 0, sizeof(from));
170
0
  if (local) {
171
0
    if (getsockname(sock, (struct sockaddr *)&from, &fromlen) == -1) {
172
0
      error("getsockname failed: %.100s", strerror(errno));
173
0
      return 0;
174
0
    }
175
0
  } else {
176
0
    if (getpeername(sock, (struct sockaddr *)&from, &fromlen) == -1) {
177
0
      debug("getpeername failed: %.100s", strerror(errno));
178
0
      return -1;
179
0
    }
180
0
  }
181
182
  /* Work around Linux IPv6 weirdness */
183
0
  if (from.ss_family == AF_INET6)
184
0
    fromlen = sizeof(struct sockaddr_in6);
185
186
  /* Non-inet sockets don't have a port number. */
187
0
  if (from.ss_family != AF_INET && from.ss_family != AF_INET6)
188
0
    return 0;
189
190
  /* Return port number. */
191
0
  if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
192
0
      strport, sizeof(strport), NI_NUMERICSERV)) != 0)
193
0
    fatal_f("getnameinfo NI_NUMERICSERV failed: %s",
194
0
        ssh_gai_strerror(r));
195
0
  return atoi(strport);
196
0
}
197
198
int
199
get_peer_port(int sock)
200
0
{
201
0
  return get_sock_port(sock, 0);
202
0
}
203
204
int
205
get_local_port(int sock)
206
0
{
207
0
  return get_sock_port(sock, 1);
208
0
}