mirror of
https://code.semirocket.science/wrapsix
synced 2024-11-10 00:01:01 +02:00
Improved creating of a socket for the resolver
Written new mechanism of wrapper from scratch in C * It listens to all ICMPv6 packets (for now) and translates them to ICMPv4 ones * It can compute the checksum of the packet as well
This commit is contained in:
parent
c27f79705f
commit
4bc68edce5
2
README
2
README
@ -11,7 +11,7 @@ WrapSix is a revolutionary piece of software that make possible to reach IPv4-on
|
|||||||
== Requirements ==
|
== Requirements ==
|
||||||
WrapSix is very simple to use. You only need:
|
WrapSix is very simple to use. You only need:
|
||||||
* GNU/Linux system
|
* GNU/Linux system
|
||||||
* Ruby (I've tested version 1.8.7)
|
* Ruby (I've tested version 1.8.6)
|
||||||
|
|
||||||
== Configuration ==
|
== Configuration ==
|
||||||
|
|
||||||
|
@ -18,11 +18,14 @@ class Resolver
|
|||||||
|
|
||||||
def start
|
def start
|
||||||
@resolver = UDPSocket.open Socket::AF_INET6
|
@resolver = UDPSocket.open Socket::AF_INET6
|
||||||
if @resolver.bind $config['resolver_ip'], 53
|
begin
|
||||||
puts "Started DNS resolver on IPv6 address #{$config['resolver_ip']}" if @debug
|
@resolver.bind $config['resolver_ip'], 53
|
||||||
else
|
#rescue Errno::EPERM
|
||||||
puts "DNS resolver not started!" if @debug
|
rescue Errno::EACCES
|
||||||
|
$stderr.puts "You have to run #{$0} as root!"
|
||||||
|
exit!
|
||||||
end
|
end
|
||||||
|
puts "Started DNS resolver on IPv6 address #{$config['resolver_ip']}" if @debug
|
||||||
|
|
||||||
loop do
|
loop do
|
||||||
puts "---------- start ----------" if @debug
|
puts "---------- start ----------" if @debug
|
||||||
|
24
wrapper/Makefile
Normal file
24
wrapper/Makefile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
CC = gcc
|
||||||
|
CFLAGS = -Wall -O0
|
||||||
|
LDFLAGS = -L/usr/lib -lpcap
|
||||||
|
|
||||||
|
all: wrapper.o process.o translate_ip.o connections.o checksum.o
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) *.o -o wrapper
|
||||||
|
|
||||||
|
wrapper.o: wrapper.c wrapper.h
|
||||||
|
$(CC) $(CFLAGS) -c wrapper.c -o wrapper.o
|
||||||
|
|
||||||
|
process.o: process.c wrapper.h translate_ip.h
|
||||||
|
$(CC) $(CFLAGS) -c process.c -o process.o
|
||||||
|
|
||||||
|
translate_ip.o: translate_ip.c translate_ip.h wrapper.h
|
||||||
|
$(CC) $(CFLAGS) -c translate_ip.c -o translate_ip.o
|
||||||
|
|
||||||
|
connections.o: connections.c wrapper.h
|
||||||
|
$(CC) $(CFLAGS) -c connections.c -o connections.o
|
||||||
|
|
||||||
|
checksum.o: checksum.c
|
||||||
|
$(CC) $(CFLAGS) -c checksum.c -o checksum.o
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f wrapper *.o
|
30
wrapper/checksum.c
Normal file
30
wrapper/checksum.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
unsigned short checksum(const void *_buf, int len)
|
||||||
|
{
|
||||||
|
const unsigned short *buf = _buf;
|
||||||
|
unsigned sum = 0;
|
||||||
|
|
||||||
|
while (len >= 2) {
|
||||||
|
sum += *buf ++;
|
||||||
|
|
||||||
|
if (sum & 0x80000000) {
|
||||||
|
sum = (sum & 0xffff) + (sum >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len) {
|
||||||
|
unsigned char temp[2];
|
||||||
|
|
||||||
|
temp[0] = *(unsigned char *) buf;
|
||||||
|
temp[1] = 0;
|
||||||
|
|
||||||
|
sum += *(unsigned short *) temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sum >> 16) {
|
||||||
|
sum = (sum & 0xffff) + (sum >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ~sum;
|
||||||
|
}
|
23
wrapper/connections.c
Normal file
23
wrapper/connections.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "wrapper.h"
|
||||||
|
|
||||||
|
void send_there(struct in_addr ip4_addr, unsigned char ttl, unsigned int type, unsigned char *payload, unsigned int paylen) {
|
||||||
|
int sock;
|
||||||
|
struct sockaddr_in sock_addr;
|
||||||
|
|
||||||
|
if ((sock = socket(AF_INET, SOCK_RAW, type)) == -1) {
|
||||||
|
fprintf(stderr, "Couldn't open RAW socket.\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
setsockopt(sock, IPPROTO_IP, IP_TTL, (const char *) &ttl, sizeof(ttl));
|
||||||
|
|
||||||
|
sock_addr.sin_family = AF_INET;
|
||||||
|
sock_addr.sin_port = 0;
|
||||||
|
sock_addr.sin_addr = ip4_addr;
|
||||||
|
|
||||||
|
sendto(sock, (char *) payload, paylen, 0, (struct sockaddr *) &sock_addr, sizeof(struct sockaddr));
|
||||||
|
|
||||||
|
close(sock);
|
||||||
|
}
|
140
wrapper/process.c
Normal file
140
wrapper/process.c
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include "wrapper.h"
|
||||||
|
#include "translate_ip.h"
|
||||||
|
|
||||||
|
void process_packet6(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
|
||||||
|
{
|
||||||
|
static int count = 1; /* packet counter */
|
||||||
|
|
||||||
|
/* declare pointers to packet headers */
|
||||||
|
const struct s_ethernet *ethernet; /* The ethernet header [1] */
|
||||||
|
const struct s_ip6 *ip; /* The IP header */
|
||||||
|
const unsigned char *payload; /* Packet payload */
|
||||||
|
|
||||||
|
printf("\nPacket number %d:\n", count);
|
||||||
|
count++;
|
||||||
|
|
||||||
|
/* define ethernet header */
|
||||||
|
ethernet = (struct s_ethernet*) (packet);
|
||||||
|
|
||||||
|
/* define/compute ip header offset */
|
||||||
|
ip = (struct s_ip6*) (packet + SIZE_ETHERNET);
|
||||||
|
|
||||||
|
payload = packet + SIZE_ETHERNET + SIZE_IP6;
|
||||||
|
|
||||||
|
/* print source and destination IP addresses */
|
||||||
|
char ip6addr[INET6_ADDRSTRLEN];
|
||||||
|
inet_ntop(AF_INET6, &ip->ip_src, ip6addr, sizeof(ip6addr));
|
||||||
|
printf(" From: %s\n", ip6addr);
|
||||||
|
/* keep the following line as the last one inet_ntop! */
|
||||||
|
inet_ntop(AF_INET6, &ip->ip_dest, ip6addr, sizeof(ip6addr));
|
||||||
|
printf(" To: %s\n", ip6addr);
|
||||||
|
|
||||||
|
/* check if this packet is ours - hardcoded for now */
|
||||||
|
char wsaddr[INET6_ADDRSTRLEN] = "fc00:1::4d4b:4c03";
|
||||||
|
if (strcmp(wsaddr, ip6addr) != 0) {
|
||||||
|
printf("==> This packet is not ours! <==\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* determine protocol */
|
||||||
|
switch (ip->next_header) {
|
||||||
|
case IPPROTO_TCP:
|
||||||
|
printf(" Protocol: TCP\n");
|
||||||
|
//process_tcp();
|
||||||
|
break;
|
||||||
|
case IPPROTO_UDP:
|
||||||
|
printf(" Protocol: UDP\n");
|
||||||
|
break;
|
||||||
|
case IPPROTO_ICMPV6:
|
||||||
|
printf(" Protocol: ICMPv6\n");
|
||||||
|
process_icmp6((struct s_ip6 *) ip, payload);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf(" Protocol: unknown\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_icmp6(struct s_ip6 *ip, const unsigned char *payload)
|
||||||
|
{
|
||||||
|
struct s_icmp *icmp;
|
||||||
|
struct in_addr ip4_addr;
|
||||||
|
|
||||||
|
unsigned char *icmp_data;
|
||||||
|
unsigned char *icmp_packet;
|
||||||
|
|
||||||
|
int packet_size;
|
||||||
|
|
||||||
|
/* extract the ICMP header */
|
||||||
|
icmp = (struct s_icmp *) (payload);
|
||||||
|
icmp_data = (unsigned char *) (payload + sizeof(icmp));
|
||||||
|
|
||||||
|
/* decide what type of ICMP we have */
|
||||||
|
switch (icmp->type) {
|
||||||
|
/* NDP */
|
||||||
|
case ICMP6_NDP_NS:
|
||||||
|
printf(" ICMP: [NDP] Neighbor Solicitation\n");
|
||||||
|
break;
|
||||||
|
case ICMP6_NDP_NA:
|
||||||
|
printf(" ICMP: [NDP] Neighbor Advertisement\n");
|
||||||
|
break;
|
||||||
|
case ICMP6_NDP_RS:
|
||||||
|
printf(" ICMP: [NDP] Router Solicitation\n");
|
||||||
|
break;
|
||||||
|
case ICMP6_NDP_RA:
|
||||||
|
printf(" ICMP: [NDP] Router Advertisement\n");
|
||||||
|
break;
|
||||||
|
case ICMP6_NDP_RM:
|
||||||
|
printf(" ICMP: [NDP] Redirect Message\n");
|
||||||
|
break;
|
||||||
|
/* ping */
|
||||||
|
case ICMP6_ECHO_REQUEST:
|
||||||
|
printf(" ICMP: Echo Request\n");
|
||||||
|
|
||||||
|
packet_size = htons(ip->len);
|
||||||
|
icmp_packet = (unsigned char *) malloc(packet_size);
|
||||||
|
|
||||||
|
if (icmp_packet == NULL) {
|
||||||
|
fprintf(stderr, "Fatal error! Lack of free memory!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct s_icmp_ping *icmp_ping = (struct s_icmp_ping *) icmp_data;
|
||||||
|
|
||||||
|
icmp->type = ICMP4_ECHO_REQUEST;
|
||||||
|
icmp->code = 0;
|
||||||
|
icmp->checksum = 0;
|
||||||
|
|
||||||
|
printf("[id;seq]:[0x%x;0x%x]\n", htons(icmp_ping->id), htons(icmp_ping->seq));
|
||||||
|
|
||||||
|
memcpy(icmp_packet, icmp, sizeof(struct s_icmp));
|
||||||
|
memcpy(icmp_packet + sizeof(struct s_icmp), icmp_data, packet_size - sizeof(struct s_icmp));
|
||||||
|
|
||||||
|
// compute the checksum :c)
|
||||||
|
icmp->checksum = checksum(icmp_packet, packet_size);
|
||||||
|
|
||||||
|
// copy this structure again - because of the checksum
|
||||||
|
memcpy(icmp_packet, icmp, sizeof(struct s_icmp));
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ICMP6_ECHO_REPLY:
|
||||||
|
printf(" ICMP: Echo Reply\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf(" ICMP: unknown: %d/0x%x\n", icmp->type, icmp->type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* where to send this ICMP */
|
||||||
|
ip4_addr = ipaddr_6to4((struct in6_addr) ip->ip_dest);
|
||||||
|
printf(" Send to: %s\n", inet_ntoa(ip4_addr));
|
||||||
|
|
||||||
|
/* send */
|
||||||
|
send_there(ip4_addr, ip->hop_limit, IPPROTO_ICMP, icmp_packet, packet_size);
|
||||||
|
|
||||||
|
free(icmp_packet);
|
||||||
|
icmp_packet = NULL;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
18
wrapper/translate_ip.c
Normal file
18
wrapper/translate_ip.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "wrapper.h"
|
||||||
|
#include "translate_ip.h"
|
||||||
|
|
||||||
|
struct in_addr ipaddr_6to4(struct in6_addr ip6_addr)
|
||||||
|
{
|
||||||
|
struct ip6addr_ip4part *addr;
|
||||||
|
struct in_addr ip4_addr;
|
||||||
|
char ip4_str[15];
|
||||||
|
|
||||||
|
/* "parse" the IPv6 addres */
|
||||||
|
addr = (struct ip6addr_ip4part *)(&ip6_addr);
|
||||||
|
|
||||||
|
/* build IPv4 address */
|
||||||
|
sprintf(ip4_str, "%d.%d.%d.%d", addr->a, addr->b, addr->c, addr->d);
|
||||||
|
inet_aton(ip4_str, &ip4_addr);
|
||||||
|
|
||||||
|
return ip4_addr;
|
||||||
|
}
|
15
wrapper/translate_ip.h
Normal file
15
wrapper/translate_ip.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef TRANSLATE_IP_H
|
||||||
|
#define TRANSLATE_IP_H
|
||||||
|
|
||||||
|
struct ip6addr_ip4part {
|
||||||
|
long double prefix;
|
||||||
|
unsigned char a;
|
||||||
|
unsigned char b;
|
||||||
|
unsigned char c;
|
||||||
|
unsigned char d;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct in_addr ipaddr_6to4(struct in6_addr ip6_addr);
|
||||||
|
//in6_addr ipaddr_4to6(in_addr ip_addr);
|
||||||
|
|
||||||
|
#endif
|
73
wrapper/wrapper.c
Normal file
73
wrapper/wrapper.c
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include "wrapper.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
|
||||||
|
char *dev = NULL; /* capture device name */
|
||||||
|
char errbuf[PCAP_ERRBUF_SIZE]; /* error buffer */
|
||||||
|
pcap_t *handle; /* packet capture handle */
|
||||||
|
|
||||||
|
//char filter_exp[] = "ip6"; /* filter expression */
|
||||||
|
char filter_exp[] = "icmp6"; /* filter expression */
|
||||||
|
struct bpf_program fp; /* compiled filter program (expression) */
|
||||||
|
bpf_u_int32 mask; /* subnet mask */
|
||||||
|
bpf_u_int32 net; /* ip */
|
||||||
|
int num_packets = 0; /* number of packets to capture */
|
||||||
|
|
||||||
|
/* find a capture device */
|
||||||
|
dev = pcap_lookupdev(errbuf);
|
||||||
|
if (dev == NULL) {
|
||||||
|
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get network number and mask associated with capture device */
|
||||||
|
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
|
||||||
|
fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
|
||||||
|
net = 0;
|
||||||
|
mask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print capture info */
|
||||||
|
printf("Device: %s\n", dev);
|
||||||
|
printf("Number of packets: %d\n", num_packets);
|
||||||
|
printf("Filter expression: %s\n", filter_exp);
|
||||||
|
|
||||||
|
/* open capture device */
|
||||||
|
handle = pcap_open_live(dev, SNAP_LEN, 1, 1, errbuf);
|
||||||
|
if (handle == NULL) {
|
||||||
|
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure we're capturing on an Ethernet device */
|
||||||
|
if (pcap_datalink(handle) != DLT_EN10MB) {
|
||||||
|
fprintf(stderr, "%s is not an Ethernet\n", dev);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compile the filter expression */
|
||||||
|
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
|
||||||
|
fprintf(stderr, "Couldn't parse filter %s: %s\n",
|
||||||
|
filter_exp, pcap_geterr(handle));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* apply the compiled filter */
|
||||||
|
if (pcap_setfilter(handle, &fp) == -1) {
|
||||||
|
fprintf(stderr, "Couldn't install filter %s: %s\n",
|
||||||
|
filter_exp, pcap_geterr(handle));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now we can set our callback function */
|
||||||
|
pcap_loop(handle, num_packets, process_packet6, NULL);
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
|
pcap_freecode(&fp);
|
||||||
|
pcap_close(handle);
|
||||||
|
|
||||||
|
printf("\nCapture complete.\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
93
wrapper/wrapper.h
Normal file
93
wrapper/wrapper.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#ifndef WRAPPER_H
|
||||||
|
#define WRAPPER_H
|
||||||
|
|
||||||
|
#include <pcap.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
/* Default snap length (maximum bytes per packet to capture) */
|
||||||
|
#define SNAP_LEN BUFSIZ
|
||||||
|
|
||||||
|
/* Ethernet headers are always exactly 14 bytes */
|
||||||
|
#define SIZE_ETHERNET 14
|
||||||
|
|
||||||
|
/* Ethernet addresses are 6 bytes */
|
||||||
|
#define ETHER_ADDR_LEN 6
|
||||||
|
|
||||||
|
/* IPv6 headers are always exactly 40 bytes */
|
||||||
|
#define SIZE_IP6 40
|
||||||
|
|
||||||
|
/* Ethernet header */
|
||||||
|
struct s_ethernet {
|
||||||
|
u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */
|
||||||
|
u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */
|
||||||
|
u_short ether_type; /* IP/ARP/RARP/... */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* IPv4 header */
|
||||||
|
|
||||||
|
/* IPv6 header */
|
||||||
|
struct s_ip6 {
|
||||||
|
unsigned char ver; /* 8 b; version */
|
||||||
|
unsigned char traffic_class; /* 8 b; traffic class */
|
||||||
|
unsigned short flow_label; /* 16 b; flow label (qos) */
|
||||||
|
unsigned short len; /* 16 b; payload length */
|
||||||
|
unsigned char next_header; /* 8 b; next header */
|
||||||
|
unsigned char hop_limit; /* 8 b; hop limit (replaces ttl) */
|
||||||
|
struct in6_addr ip_src; /* 128 b; source address */
|
||||||
|
struct in6_addr ip_dest; /* 128 b; destination address */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TCP structure - only needed fields! */
|
||||||
|
struct s_tcp {
|
||||||
|
unsigned short port_src; /* 16 b; source port */
|
||||||
|
unsigned short port_dest; /* 16 b; destination port */
|
||||||
|
long double data1; /* 96 b; first data segment */
|
||||||
|
unsigned short checksum; /* 16 b */
|
||||||
|
unsigned short data2; /* 16 b; the rest (urgent pointer here) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* UDP structure */
|
||||||
|
|
||||||
|
/* ICMP header structure */
|
||||||
|
struct s_icmp {
|
||||||
|
unsigned char type; /* 8 b; ICMP type */
|
||||||
|
unsigned char code; /* 8 b; further specification of ICMP type */
|
||||||
|
unsigned short checksum; /* 16 b */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ICMP - ping structure */
|
||||||
|
struct s_icmp_ping {
|
||||||
|
unsigned short id; /* 16 b; ID value for ECHO REPLY */
|
||||||
|
unsigned short seq; /* 16 b; sequence value for ECHO REPLY */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ICMP types */
|
||||||
|
#define ICMP4_ECHO_REQUEST 0x8
|
||||||
|
#define ICMP4_ECHO_REPLY 0x0
|
||||||
|
|
||||||
|
/* ICMPv6 types */
|
||||||
|
#define ICMP6_ECHO_REQUEST 0x80
|
||||||
|
#define ICMP6_ECHO_REPLY 0x81
|
||||||
|
#define ICMP6_NDP_RS 0x85
|
||||||
|
#define ICMP6_NDP_RA 0x86
|
||||||
|
#define ICMP6_NDP_NS 0x87
|
||||||
|
#define ICMP6_NDP_NA 0x88
|
||||||
|
#define ICMP6_NDP_RM 0x89
|
||||||
|
|
||||||
|
/* Prototypes */
|
||||||
|
void process_packet6(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
|
||||||
|
void process_icmp6(struct s_ip6 *ip, const unsigned char *payload);
|
||||||
|
|
||||||
|
void send_there(struct in_addr ip4_addr, unsigned char ttl, unsigned int type, unsigned char *payload, unsigned int paylen);
|
||||||
|
|
||||||
|
unsigned short checksum(const void *_buf, int len);
|
||||||
|
|
||||||
|
#endif
|
@ -48,13 +48,13 @@ trap "QUIT" do; exit; end
|
|||||||
|
|
||||||
services = []
|
services = []
|
||||||
### Start DNS resolver function
|
### Start DNS resolver function
|
||||||
if $config['resolver']
|
if $config['resolver'] == 1
|
||||||
$resolver = Resolver.new
|
$resolver = Resolver.new
|
||||||
services << Thread.start do; $resolver.start; end
|
services << Thread.start do; $resolver.start; end
|
||||||
end
|
end
|
||||||
|
|
||||||
### Start IPv6-to-IPv4 wrapper
|
### Start IPv6-to-IPv4 wrapper
|
||||||
if $config['wrapper']
|
if $config['wrapper'] == 1
|
||||||
$wrapper = Wrapper.new
|
$wrapper = Wrapper.new
|
||||||
services << Thread.start do; $wrapper.start; end
|
services << Thread.start do; $wrapper.start; end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user