-if (ret >= 16 && memcmp(&hdr.v2, v2sig, 12) == 0)
- {
- uint8_t cmd = (hdr.v2.ver_cmd & 0x0f);
-
- switch (cmd)
- {
- case 0x01: /* PROXY command */
- switch (hdr.v2.fam)
- {
- case 0x11: /* TCPv4 address type */
- iptype = US"IPv4";
- tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr;
- inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
- if (!string_is_ip_address(US tmpip, NULL))
- {
- DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
- goto proxyfail;
- }
- proxy_local_address = sender_host_address;
- sender_host_address = string_copy(US tmpip);
- tmpport = ntohs(hdr.v2.addr.ip4.src_port);
- proxy_local_port = sender_host_port;
- sender_host_port = tmpport;
- /* Save dest ip/port */
- tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.dst_addr;
- inet_ntop(AF_INET, &tmpaddr.sin_addr, CS &tmpip, sizeof(tmpip));
- if (!string_is_ip_address(US tmpip, NULL))
- {
- DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype);
- goto proxyfail;
- }
- proxy_external_address = string_copy(US tmpip);
- tmpport = ntohs(hdr.v2.addr.ip4.dst_port);
- proxy_external_port = tmpport;
- goto done;
- case 0x21: /* TCPv6 address type */
- iptype = US"IPv6";
- memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16);
- inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
- if (!string_is_ip_address(US tmpip6, NULL))
- {
- DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
- goto proxyfail;
- }
- proxy_local_address = sender_host_address;
- sender_host_address = string_copy(US tmpip6);
- tmpport = ntohs(hdr.v2.addr.ip6.src_port);
- proxy_local_port = sender_host_port;
- sender_host_port = tmpport;
- /* Save dest ip/port */
- memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.dst_addr, 16);
- inet_ntop(AF_INET6, &tmpaddr6.sin6_addr, CS &tmpip6, sizeof(tmpip6));
- if (!string_is_ip_address(US tmpip6, NULL))
- {
- DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype);
- goto proxyfail;
- }
- proxy_external_address = string_copy(US tmpip6);
- tmpport = ntohs(hdr.v2.addr.ip6.dst_port);
- proxy_external_port = tmpport;
- goto done;
- default:
- DEBUG(D_receive)
- debug_printf("Unsupported PROXYv2 connection type: 0x%02x\n",
- hdr.v2.fam);
- goto proxyfail;
- }
- /* Unsupported protocol, keep local connection address */
- break;
- case 0x00: /* LOCAL command */
- /* Keep local connection address for LOCAL */
- iptype = US"local";
- break;
- default:
- DEBUG(D_receive)
- debug_printf("Unsupported PROXYv2 command: 0x%x\n", cmd);
- goto proxyfail;
- }
- }
-else if (ret >= 8 && memcmp(hdr.v1.line, "PROXY", 5) == 0)
- {
- uschar *p;
- uschar *end;
- uschar *sp; /* Utility variables follow */
- int tmp_port;
- int r2;
- char *endc;
-
- /* get the rest of the line */
- r2 = swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret);
- if (r2 == -1)
- goto proxyfail;
- ret += r2;
-
- p = string_copy(hdr.v1.line);
- end = memchr(p, '\r', ret - 1);
-
- if (!end || (end == (uschar*)&hdr + ret) || end[1] != '\n')
- {
- DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n");
- goto proxyfail;
- }
- *end = '\0'; /* Terminate the string */
- size = end + 2 - p; /* Skip header + CRLF */
- DEBUG(D_receive) debug_printf("Detected PROXYv1 header\n");
- DEBUG(D_receive) debug_printf("Bytes read not within PROXY header: %d\n", ret - size);
- /* Step through the string looking for the required fields. Ensure
- strict adherence to required formatting, exit for any error. */
- p += 5;
- if (!isspace(*(p++)))
- {
- DEBUG(D_receive) debug_printf("Missing space after PROXY command\n");
- goto proxyfail;
- }
- if (!Ustrncmp(p, CCS"TCP4", 4))
- iptype = US"IPv4";
- else if (!Ustrncmp(p,CCS"TCP6", 4))
- iptype = US"IPv6";
- else if (!Ustrncmp(p,CCS"UNKNOWN", 7))
- {
- iptype = US"Unknown";
- goto done;
- }
- else
- {
- DEBUG(D_receive) debug_printf("Invalid TCP type\n");
- goto proxyfail;
- }
-
- p += Ustrlen(iptype);
- if (!isspace(*(p++)))
- {
- DEBUG(D_receive) debug_printf("Missing space after TCP4/6 command\n");
- goto proxyfail;
- }
- /* Find the end of the arg */
- if ((sp = Ustrchr(p, ' ')) == NULL)
- {
- DEBUG(D_receive)
- debug_printf("Did not find proxied src %s\n", iptype);
- goto proxyfail;
- }
- *sp = '\0';
- if(!string_is_ip_address(p, NULL))
- {
- DEBUG(D_receive)
- debug_printf("Proxied src arg is not an %s address\n", iptype);
- goto proxyfail;
- }
- proxy_local_address = sender_host_address;
- sender_host_address = p;
- p = sp + 1;
- if ((sp = Ustrchr(p, ' ')) == NULL)
- {
- DEBUG(D_receive)
- debug_printf("Did not find proxy dest %s\n", iptype);
- goto proxyfail;
- }
- *sp = '\0';
- if(!string_is_ip_address(p, NULL))
- {
- DEBUG(D_receive)
- debug_printf("Proxy dest arg is not an %s address\n", iptype);
- goto proxyfail;
- }
- proxy_external_address = p;
- p = sp + 1;
- if ((sp = Ustrchr(p, ' ')) == NULL)
- {
- DEBUG(D_receive) debug_printf("Did not find proxied src port\n");
- goto proxyfail;
- }
- *sp = '\0';
- tmp_port = strtol(CCS p, &endc, 10);
- if (*endc || tmp_port == 0)
- {
- DEBUG(D_receive)
- debug_printf("Proxied src port '%s' not an integer\n", p);
- goto proxyfail;
- }
- proxy_local_port = sender_host_port;
- sender_host_port = tmp_port;
- p = sp + 1;
- if ((sp = Ustrchr(p, '\0')) == NULL)
- {
- DEBUG(D_receive) debug_printf("Did not find proxy dest port\n");
- goto proxyfail;
- }
- tmp_port = strtol(CCS p, &endc, 10);
- if (*endc || tmp_port == 0)
- {
- DEBUG(D_receive)
- debug_printf("Proxy dest port '%s' not an integer\n", p);
- goto proxyfail;
- }
- proxy_external_port = tmp_port;
- /* Already checked for /r /n above. Good V1 header received. */
- }
-else
- {
- /* Wrong protocol */
- DEBUG(D_receive) debug_printf("Invalid proxy protocol version negotiation\n");
- (void) swallow_until_crlf(fd, (uschar*)&hdr, ret, sizeof(hdr)-ret);
- goto proxyfail;
- }
-
-done:
- DEBUG(D_receive)
- debug_printf("Valid %s sender from Proxy Protocol header\n", iptype);
- yield = proxy_session;
-
-/* Don't flush any potential buffer contents. Any input on proxyfail
-should cause a synchronization failure */
-
-proxyfail:
- restore_socket_timeout(fd, get_ok, &tvtmp, vslen);
-
-bad:
- if (yield)
- {
- sender_host_name = NULL;
- (void) host_name_lookup();
- host_build_sender_fullhost();
- }
- else
- {
- proxy_session_failed = TRUE;
- DEBUG(D_receive)
- debug_printf("Failure to extract proxied host, only QUIT allowed\n");
- }
-
-return;
-}
-#endif
-
-/*************************************************
-* Read one command line *
-*************************************************/
-
-/* Strictly, SMTP commands coming over the net are supposed to end with CRLF.
-There are sites that don't do this, and in any case internal SMTP probably
-should check only for LF. Consequently, we check here for LF only. The line
-ends up with [CR]LF removed from its end. If we get an overlong line, treat as
-an unknown command. The command is read into the global smtp_cmd_buffer so that
-it is available via $smtp_command.
-
-The character reading routine sets up a timeout for each block actually read
-from the input (which may contain more than one command). We set up a special
-signal handler that closes down the session on a timeout. Control does not
-return when it runs.
-
-Arguments:
- check_sync if TRUE, check synchronization rules if global option is TRUE
- buffer_lim maximum to buffer in lower layer
-
-Returns: a code identifying the command (enumerated above)
-*/
-
-static int
-smtp_read_command(BOOL check_sync, unsigned buffer_lim)
-{
-int c;
-int ptr = 0;
-smtp_cmd_list *p;
-BOOL hadnull = FALSE;
-
-os_non_restarting_signal(SIGALRM, command_timeout_handler);
-
-while ((c = (receive_getc)(buffer_lim)) != '\n' && c != EOF)
- {
- if (ptr >= SMTP_CMD_BUFFER_SIZE)
- {
- os_non_restarting_signal(SIGALRM, sigalrm_handler);
- return OTHER_CMD;
- }
- if (c == 0)
- {
- hadnull = TRUE;
- c = '?';
- }
- smtp_cmd_buffer[ptr++] = c;
- }
-
-receive_linecount++; /* For BSMTP errors */
-os_non_restarting_signal(SIGALRM, sigalrm_handler);
-
-/* If hit end of file, return pseudo EOF command. Whether we have a
-part-line already read doesn't matter, since this is an error state. */
-
-if (c == EOF) return EOF_CMD;
-
-/* Remove any CR and white space at the end of the line, and terminate the
-string. */
-
-while (ptr > 0 && isspace(smtp_cmd_buffer[ptr-1])) ptr--;
-smtp_cmd_buffer[ptr] = 0;
-
-DEBUG(D_receive) debug_printf("SMTP<< %s\n", smtp_cmd_buffer);
-
-/* NULLs are not allowed in SMTP commands */
-
-if (hadnull) return BADCHAR_CMD;
-
-/* Scan command list and return identity, having set the data pointer
-to the start of the actual data characters. Check for SMTP synchronization
-if required. */
-
-for (p = cmd_list; p < cmd_list_end; p++)
- {
-#ifdef SUPPORT_PROXY
- /* Only allow QUIT command if Proxy Protocol parsing failed */
- if (proxy_session && proxy_session_failed && p->cmd != QUIT_CMD)
- continue;
-#endif
- if ( p->len
- && strncmpic(smtp_cmd_buffer, US p->name, p->len) == 0
- && ( smtp_cmd_buffer[p->len-1] == ':' /* "mail from:" or "rcpt to:" */
- || smtp_cmd_buffer[p->len] == 0
- || smtp_cmd_buffer[p->len] == ' '
- ) )
- {
- if (smtp_inptr < smtp_inend && /* Outstanding input */
- p->cmd < sync_cmd_limit && /* Command should sync */
- check_sync && /* Local flag set */
- smtp_enforce_sync && /* Global flag set */
- sender_host_address != NULL && /* Not local input */
- !sender_host_notsocket) /* Really is a socket */
- return BADSYN_CMD;
-
- /* The variables $smtp_command and $smtp_command_argument point into the
- unmodified input buffer. A copy of the latter is taken for actual
- processing, so that it can be chopped up into separate parts if necessary,
- for example, when processing a MAIL command options such as SIZE that can
- follow the sender address. */
-
- smtp_cmd_argument = smtp_cmd_buffer + p->len;
- while (isspace(*smtp_cmd_argument)) smtp_cmd_argument++;
- Ustrcpy(smtp_data_buffer, smtp_cmd_argument);
- smtp_cmd_data = smtp_data_buffer;
-
- /* Count non-mail commands from those hosts that are controlled in this
- way. The default is all hosts. We don't waste effort checking the list
- until we get a non-mail command, but then cache the result to save checking
- again. If there's a DEFER while checking the host, assume it's in the list.
-
- Note that one instance of RSET, EHLO/HELO, and STARTTLS is allowed at the
- start of each incoming message by fiddling with the value in the table. */
-
- if (!p->is_mail_cmd)
- {
- if (count_nonmail == TRUE_UNSET) count_nonmail =
- verify_check_host(&smtp_accept_max_nonmail_hosts) != FAIL;
- if (count_nonmail && ++nonmail_command_count > smtp_accept_max_nonmail)
- return TOO_MANY_NONMAIL_CMD;
- }
-
- /* If there is data for a command that does not expect it, generate the
- error here. */
-
- return (p->has_arg || *smtp_cmd_data == 0)? p->cmd : BADARG_CMD;
- }
- }
-
-#ifdef SUPPORT_PROXY
-/* Only allow QUIT command if Proxy Protocol parsing failed */
-if (proxy_session && proxy_session_failed)
- return PROXY_FAIL_IGNORE_CMD;
-#endif