mirror of
https://github.com/coturn/coturn.git
synced 2025-11-03 16:31:22 +01:00
support of --acme-redirect <URL>
This commit is contained in:
parent
d4686750ee
commit
12c7d19a47
@ -99,6 +99,83 @@ const char* get_http_date_header()
|
||||
return buffer_header;
|
||||
}
|
||||
|
||||
static int is_acme_req(char *req, size_t len) {
|
||||
static const char *A = " - 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ _ abcdefghijklmnopqrstuvwxyz ";
|
||||
int c, i, k;
|
||||
|
||||
// Check first request line. Should be like: GET path HTTP/1.x
|
||||
if (strncmp(req, "GET /.well-known/acme-challenge/", 32))
|
||||
return -1;
|
||||
// Usually (for LE) the "method path" is 32 + 43 = 55 chars. But other
|
||||
// implementations may choose longer pathes. We define PATHMAX = 127 chars
|
||||
// to be prepared for "DoS" attacks (STUN msg size max. is ~ 64K).
|
||||
len =- 21; // min size of trailing headers
|
||||
if (len > 131)
|
||||
len = 131;
|
||||
for (i=32; i < (int) len; i++) {
|
||||
// find the end of the path
|
||||
if (req[i] != ' ')
|
||||
continue;
|
||||
// consider path < 10 chars invalid. Also we wanna see a "trailer".
|
||||
if (i < 42 || strncmp(req + i, " HTTP/1.", 8))
|
||||
return -2;
|
||||
// finally check for allowed chars
|
||||
for (k=32; k < i; k++) {
|
||||
c = req[k];
|
||||
if ((c > 127) || (A[c] == ' '))
|
||||
return -3;
|
||||
}
|
||||
// all checks passed: sufficient for us to answer with a redirect
|
||||
return i;
|
||||
}
|
||||
return -4; // end of path not found
|
||||
}
|
||||
|
||||
int try_acme_redirect(char *req, size_t len, const char *url,
|
||||
ioa_socket_handle s)
|
||||
{
|
||||
static const char *HTML = "<html><head><title>301 Moved Permanently</title></head><body><h1>301 Moved Permanently</h1></body></html>";
|
||||
char http_response[1024];
|
||||
int plen, rlen;
|
||||
|
||||
if (url == NULL || url[0] == '\0' || req == NULL || s == 0 )
|
||||
return 1;
|
||||
if (len < 64 || len > 512 || (plen = is_acme_req(req, len)) < 33)
|
||||
return 2;
|
||||
|
||||
req[plen] = '\0';
|
||||
snprintf(http_response, sizeof(http_response) - 1,
|
||||
"HTTP/1.1 301 Moved Permanently\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: %ld\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Location: %s%s\r\n"
|
||||
"\r\n%s", strlen(HTML), url, req + 32, HTML);
|
||||
|
||||
rlen = strlen(http_response);
|
||||
|
||||
// Variant A: direkt write, no eventbuf stuff
|
||||
if (write(s->fd, http_response, rlen) == -1) {
|
||||
perror("Sending redirect failed");
|
||||
} else if (((turn_turnserver *)s->session->server)->verbose) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ACME redirect to %s%s\n",
|
||||
url, req + 32);
|
||||
}
|
||||
|
||||
req[plen] = ' ';
|
||||
|
||||
// Variant B: via eventbuf does not send anything for whatever reason
|
||||
/*
|
||||
set_ioa_socket_app_type(s, HTTP_CLIENT_SOCKET);
|
||||
ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(s->e);
|
||||
uint8_t *data = ioa_network_buffer_data(nbh);
|
||||
bcopy(http_response, data, rlen);
|
||||
ioa_network_buffer_set_size(nbh, rlen);
|
||||
send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE, NULL);
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
///////////////////////////////////////////////
|
||||
|
||||
static struct headers_list * post_parse(char *data, size_t data_len)
|
||||
|
||||
@ -1673,25 +1673,25 @@ static void read_config_file(int argc, char **argv, int pass)
|
||||
|
||||
if(pass == 0) {
|
||||
|
||||
if (argv) {
|
||||
int i = 0;
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-c")) {
|
||||
if (i < argc - 1) {
|
||||
STRCPY(config_file, argv[i + 1]);
|
||||
} else {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Wrong usage of -c option\n");
|
||||
if (argv) {
|
||||
int i = 0;
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-c")) {
|
||||
if (i < argc - 1) {
|
||||
STRCPY(config_file, argv[i + 1]);
|
||||
} else {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Wrong usage of -c option\n");
|
||||
}
|
||||
} else if (!strcmp(argv[i], "-n")) {
|
||||
turn_params.do_not_use_config_file = 1;
|
||||
config_file[0]=0;
|
||||
return;
|
||||
} else if (!strcmp(argv[i], "-h")) {
|
||||
printf("\n%s\n",Usage);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(argv[i], "-n")) {
|
||||
turn_params.do_not_use_config_file = 1;
|
||||
config_file[0]=0;
|
||||
return;
|
||||
} else if (!strcmp(argv[i], "-h")) {
|
||||
printf("\n%s\n",Usage);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!turn_params.do_not_use_config_file && config_file[0]) {
|
||||
@ -1728,7 +1728,7 @@ static void read_config_file(int argc, char **argv, int pass)
|
||||
STRCPY(sarg, s);
|
||||
if (parse_arg_string(sarg, &c, &value) < 0) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Bad configuration format: %s\n",
|
||||
sarg);
|
||||
sarg);
|
||||
} else if((pass == 0) && (c == 'l')) {
|
||||
set_logfile(value);
|
||||
} else if((pass==0) && (c==NO_STDOUT_LOG_OPT)) {
|
||||
@ -1742,9 +1742,9 @@ static void read_config_file(int argc, char **argv, int pass)
|
||||
} else if ((pass==0) && (c==NEW_LOG_TIMESTAMP_FORMAT_OPT)) {
|
||||
set_turn_log_timestamp_format(value);
|
||||
} else if((pass == 0) && (c != 'u')) {
|
||||
set_option(c, value);
|
||||
set_option(c, value);
|
||||
} else if((pass > 0) && (c == 'u')) {
|
||||
set_option(c, value);
|
||||
set_option(c, value);
|
||||
}
|
||||
if (s[slen - 1] == 59) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Check config! The following line ends with semicolon: \"%s\" \n",s);
|
||||
@ -1757,7 +1757,7 @@ static void read_config_file(int argc, char **argv, int pass)
|
||||
|
||||
} else
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: Cannot find config file: %s. Default and command-line settings will be used.\n",
|
||||
config_file);
|
||||
config_file);
|
||||
|
||||
if (full_path_to_config_file) {
|
||||
free(full_path_to_config_file);
|
||||
@ -1770,7 +1770,7 @@ static int disconnect_database(void)
|
||||
{
|
||||
const turn_dbdriver_t * dbd = get_dbdriver();
|
||||
if (dbd && dbd->disconnect) {
|
||||
dbd->disconnect();
|
||||
dbd->disconnect();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1801,183 +1801,183 @@ static int adminmain(int argc, char **argv)
|
||||
|
||||
while (((c = getopt_long(argc, argv, ADMIN_OPTIONS, uo.u.o, NULL)) != -1)) {
|
||||
switch (c){
|
||||
case 'P':
|
||||
if(pwd[0]) {
|
||||
char result[257];
|
||||
generate_new_enc_password((char*)pwd, result);
|
||||
printf("%s\n",result);
|
||||
exit(0);
|
||||
}
|
||||
print_enc_password = 1;
|
||||
break;
|
||||
case 'E':
|
||||
print_enc_aes_password = 1;
|
||||
break;
|
||||
case 'g':
|
||||
ct = TA_SET_REALM_OPTION;
|
||||
break;
|
||||
case 'G':
|
||||
ct = TA_LIST_REALM_OPTIONS;
|
||||
break;
|
||||
case ADMIN_USER_QUOTA_OPT:
|
||||
po.user_quota = (vint)atoi(optarg);
|
||||
break;
|
||||
case ADMIN_TOTAL_QUOTA_OPT:
|
||||
po.total_quota = (vint)atoi(optarg);
|
||||
break;
|
||||
case ADMIN_MAX_BPS_OPT:
|
||||
po.max_bps = (vint)atoi(optarg);
|
||||
break;
|
||||
case 'O':
|
||||
ct = TA_ADD_ORIGIN;
|
||||
break;
|
||||
case 'R':
|
||||
ct = TA_DEL_ORIGIN;
|
||||
break;
|
||||
case 'I':
|
||||
ct = TA_LIST_ORIGINS;
|
||||
break;
|
||||
case 'o':
|
||||
STRCPY(origin,optarg);
|
||||
break;
|
||||
case 'k':
|
||||
ct = TA_PRINT_KEY;
|
||||
break;
|
||||
case 'a':
|
||||
ct = TA_UPDATE_USER;
|
||||
break;
|
||||
case 'd':
|
||||
ct = TA_DELETE_USER;
|
||||
break;
|
||||
case 'A':
|
||||
ct = TA_UPDATE_USER;
|
||||
is_admin = 1;
|
||||
break;
|
||||
case 'D':
|
||||
ct = TA_DELETE_USER;
|
||||
is_admin = 1;
|
||||
break;
|
||||
case 'l':
|
||||
ct = TA_LIST_USERS;
|
||||
break;
|
||||
case 'L':
|
||||
ct = TA_LIST_USERS;
|
||||
is_admin = 1;
|
||||
break;
|
||||
case 's':
|
||||
ct = TA_SET_SECRET;
|
||||
STRCPY(secret,optarg);
|
||||
break;
|
||||
case 'S':
|
||||
ct = TA_SHOW_SECRET;
|
||||
break;
|
||||
case 'X':
|
||||
ct = TA_DEL_SECRET;
|
||||
if(optarg)
|
||||
STRCPY(secret,optarg);
|
||||
break;
|
||||
case DEL_ALL_AUTH_SECRETS_OPT:
|
||||
ct = TA_DEL_SECRET;
|
||||
break;
|
||||
case 'P':
|
||||
if(pwd[0]) {
|
||||
char result[257];
|
||||
generate_new_enc_password((char*)pwd, result);
|
||||
printf("%s\n",result);
|
||||
exit(0);
|
||||
}
|
||||
print_enc_password = 1;
|
||||
break;
|
||||
case 'E':
|
||||
print_enc_aes_password = 1;
|
||||
break;
|
||||
case 'g':
|
||||
ct = TA_SET_REALM_OPTION;
|
||||
break;
|
||||
case 'G':
|
||||
ct = TA_LIST_REALM_OPTIONS;
|
||||
break;
|
||||
case ADMIN_USER_QUOTA_OPT:
|
||||
po.user_quota = (vint)atoi(optarg);
|
||||
break;
|
||||
case ADMIN_TOTAL_QUOTA_OPT:
|
||||
po.total_quota = (vint)atoi(optarg);
|
||||
break;
|
||||
case ADMIN_MAX_BPS_OPT:
|
||||
po.max_bps = (vint)atoi(optarg);
|
||||
break;
|
||||
case 'O':
|
||||
ct = TA_ADD_ORIGIN;
|
||||
break;
|
||||
case 'R':
|
||||
ct = TA_DEL_ORIGIN;
|
||||
break;
|
||||
case 'I':
|
||||
ct = TA_LIST_ORIGINS;
|
||||
break;
|
||||
case 'o':
|
||||
STRCPY(origin,optarg);
|
||||
break;
|
||||
case 'k':
|
||||
ct = TA_PRINT_KEY;
|
||||
break;
|
||||
case 'a':
|
||||
ct = TA_UPDATE_USER;
|
||||
break;
|
||||
case 'd':
|
||||
ct = TA_DELETE_USER;
|
||||
break;
|
||||
case 'A':
|
||||
ct = TA_UPDATE_USER;
|
||||
is_admin = 1;
|
||||
break;
|
||||
case 'D':
|
||||
ct = TA_DELETE_USER;
|
||||
is_admin = 1;
|
||||
break;
|
||||
case 'l':
|
||||
ct = TA_LIST_USERS;
|
||||
break;
|
||||
case 'L':
|
||||
ct = TA_LIST_USERS;
|
||||
is_admin = 1;
|
||||
break;
|
||||
case 's':
|
||||
ct = TA_SET_SECRET;
|
||||
STRCPY(secret,optarg);
|
||||
break;
|
||||
case 'S':
|
||||
ct = TA_SHOW_SECRET;
|
||||
break;
|
||||
case 'X':
|
||||
ct = TA_DEL_SECRET;
|
||||
if(optarg)
|
||||
STRCPY(secret,optarg);
|
||||
break;
|
||||
case DEL_ALL_AUTH_SECRETS_OPT:
|
||||
ct = TA_DEL_SECRET;
|
||||
break;
|
||||
#if !defined(TURN_NO_SQLITE)
|
||||
case 'b':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_SQLITE;
|
||||
break;
|
||||
case 'b':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_SQLITE;
|
||||
break;
|
||||
#endif
|
||||
#if !defined(TURN_NO_PQ)
|
||||
case 'e':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_PQ;
|
||||
break;
|
||||
case 'e':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_PQ;
|
||||
break;
|
||||
#endif
|
||||
#if !defined(TURN_NO_MYSQL)
|
||||
case 'M':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MYSQL;
|
||||
break;
|
||||
case 'M':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MYSQL;
|
||||
break;
|
||||
#endif
|
||||
#if !defined(TURN_NO_MONGO)
|
||||
case 'J':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MONGO;
|
||||
break;
|
||||
case 'J':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MONGO;
|
||||
break;
|
||||
#endif
|
||||
#if !defined(TURN_NO_HIREDIS)
|
||||
case 'N':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_REDIS;
|
||||
break;
|
||||
case 'N':
|
||||
STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg);
|
||||
turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_REDIS;
|
||||
break;
|
||||
#endif
|
||||
case 'u':
|
||||
STRCPY(user,optarg);
|
||||
if(!is_secure_string((uint8_t*)user,1)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name structure or symbols, choose another name: %s\n",user);
|
||||
exit(-1);
|
||||
}
|
||||
if(SASLprep((uint8_t*)user)<0) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user);
|
||||
exit(-1);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
set_default_realm_name(optarg);
|
||||
STRCPY(realm,optarg);
|
||||
if(SASLprep((uint8_t*)realm)<0) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong realm: %s\n",realm);
|
||||
exit(-1);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
STRCPY(pwd,optarg);
|
||||
if(SASLprep((uint8_t*)pwd)<0) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password: %s\n",pwd);
|
||||
exit(-1);
|
||||
}
|
||||
if(print_enc_password) {
|
||||
char result[257];
|
||||
generate_new_enc_password((char*)pwd, result);
|
||||
printf("%s\n",result);
|
||||
exit(0);
|
||||
}
|
||||
if(print_enc_aes_password){
|
||||
encrypt_aes_128(pwd, generated_key);
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
generate_aes_128_key(optarg, generated_key);
|
||||
exit(0);
|
||||
break;
|
||||
case 'f':
|
||||
fptr = fopen((char*)optarg, "r");
|
||||
if(fptr == NULL){
|
||||
printf("No such file like %s\n", (char*)optarg);
|
||||
}
|
||||
else{
|
||||
fseek (fptr, 0, SEEK_SET);
|
||||
rc = fread(generated_key, sizeof(char), 16, fptr);
|
||||
if( rc == 0 ){
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key file is empty\n",__FUNCTION__);
|
||||
case 'u':
|
||||
STRCPY(user,optarg);
|
||||
if(!is_secure_string((uint8_t*)user,1)) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name structure or symbols, choose another name: %s\n",user);
|
||||
exit(-1);
|
||||
}
|
||||
if(SASLprep((uint8_t*)user)<0) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user);
|
||||
exit(-1);
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
set_default_realm_name(optarg);
|
||||
STRCPY(realm,optarg);
|
||||
if(SASLprep((uint8_t*)realm)<0) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong realm: %s\n",realm);
|
||||
exit(-1);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
STRCPY(pwd,optarg);
|
||||
if(SASLprep((uint8_t*)pwd)<0) {
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password: %s\n",pwd);
|
||||
exit(-1);
|
||||
}
|
||||
if(print_enc_password) {
|
||||
char result[257];
|
||||
generate_new_enc_password((char*)pwd, result);
|
||||
printf("%s\n",result);
|
||||
exit(0);
|
||||
}
|
||||
if(print_enc_aes_password){
|
||||
encrypt_aes_128(pwd, generated_key);
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
generate_aes_128_key(optarg, generated_key);
|
||||
exit(0);
|
||||
break;
|
||||
case 'f':
|
||||
fptr = fopen((char*)optarg, "r");
|
||||
if(fptr == NULL){
|
||||
printf("No such file like %s\n", (char*)optarg);
|
||||
}
|
||||
else{
|
||||
if( rc != 16 ){
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key length is not enough\n",__FUNCTION__);
|
||||
fseek (fptr, 0, SEEK_SET);
|
||||
rc = fread(generated_key, sizeof(char), 16, fptr);
|
||||
if( rc == 0 ){
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key file is empty\n",__FUNCTION__);
|
||||
}
|
||||
else{
|
||||
if( rc != 16 ){
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key length is not enough\n",__FUNCTION__);
|
||||
}
|
||||
}
|
||||
fclose (fptr);
|
||||
}
|
||||
fclose (fptr);
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
decrypt_aes_128((char*)optarg, generated_key);
|
||||
exit(0);
|
||||
case 'h':
|
||||
printf("\n%s\n", AdminUsage);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"\n%s\n", AdminUsage);
|
||||
exit(-1);
|
||||
break;
|
||||
case 'v':
|
||||
decrypt_aes_128((char*)optarg, generated_key);
|
||||
exit(0);
|
||||
case 'h':
|
||||
printf("\n%s\n", AdminUsage);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"\n%s\n", AdminUsage);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2021,16 +2021,16 @@ static void print_features(unsigned long mfn)
|
||||
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n==== Show him the instruments, Practical Frost: ====\n\n");
|
||||
|
||||
/*
|
||||
Frost stepped forward and opened the polished case with a theatrical
|
||||
flourish. It was a masterful piece of craftsmanship. As the lid was
|
||||
pulled back, the many trays inside lifted and fanned out, displaying
|
||||
Glokta’s tools in all their gruesome glory. There were blades of every
|
||||
size and shape, needles curved and straight, bottles of oil and acid,
|
||||
nails and screws, clamps and pliers, saws, hammers, chisels. Metal, wood
|
||||
and glass glittered in the bright lamplight, all polished to mirror
|
||||
brightness and honed to a murderous sharpness.
|
||||
*/
|
||||
/*
|
||||
Frost stepped forward and opened the polished case with a theatrical
|
||||
flourish. It was a masterful piece of craftsmanship. As the lid was
|
||||
pulled back, the many trays inside lifted and fanned out, displaying
|
||||
Glokta’s tools in all their gruesome glory. There were blades of every
|
||||
size and shape, needles curved and straight, bottles of oil and acid,
|
||||
nails and screws, clamps and pliers, saws, hammers, chisels. Metal, wood
|
||||
and glass glittered in the bright lamplight, all polished to mirror
|
||||
brightness and honed to a murderous sharpness.
|
||||
*/
|
||||
|
||||
#if !TLS_SUPPORTED
|
||||
TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS is not supported\n");
|
||||
|
||||
@ -285,6 +285,7 @@ int get_default_protocol_port(const char* scheme, size_t slen);
|
||||
///////////// HTTP ////////////////////
|
||||
|
||||
void handle_http_echo(ioa_socket_handle s);
|
||||
int try_acme_redirect(char *req, size_t len, const char *url, ioa_socket_handle s);
|
||||
|
||||
///////////// ACME /////////////////////
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user