mirror of https://github.com/dirtbags/fluffy.git
version of hd in go
This commit is contained in:
parent
306f0c6264
commit
c47e11c329
13
Makefile
13
Makefile
|
@ -1,13 +1,20 @@
|
||||||
CFLAGS = -Wall -Werror -Wno-unused-variable
|
CFLAGS = -Wall -Werror
|
||||||
TARGETS = pmerge puniq p4split pcat
|
TARGETS = pmerge puniq p4split pcat
|
||||||
TARGETS += hd pyesc printfesc xor unhex
|
TARGETS += hd pyesc printfesc xor unhex
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
%: %.go
|
||||||
|
go build $<
|
||||||
|
|
||||||
pmerge: pmerge.o pcap.o
|
pmerge: pmerge.o pcap.o
|
||||||
|
|
||||||
puniq: puniq.o pcap.o
|
puniq: puniq.o pcap.o
|
||||||
p4split: p4split.c pcap.o
|
|
||||||
pcat: pcat.c pcap.o stream.o
|
p4split: p4split.o pcap.o
|
||||||
|
|
||||||
|
pcat: pcat.o pcap.o stream.o
|
||||||
|
pcat.o: CFLAGS += -Wno-unused-variable
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGETS) *.o
|
rm -f $(TARGETS) *.o
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
The Fluffy Suite
|
||||||
|
============
|
||||||
|
|
||||||
|
Fluffy was begun in April 2011 in Tennessee,
|
||||||
|
as a replacement for the aging "dirtbags.ip" codebase.
|
||||||
|
It is comprised of multiple small standalone binaries,
|
||||||
|
which are meant to be chained together,
|
||||||
|
either on the command-line or from a shell script,
|
||||||
|
to create a more powerful (and specific) piece of software.
|
||||||
|
|
||||||
|
Usually, a program expects input on stdin,
|
||||||
|
and produces output on stdout.
|
||||||
|
Flags are sparse by design.
|
||||||
|
|
||||||
|
|
||||||
|
hd -- Hex Dump
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Like the normal hd,
|
||||||
|
but with unicode characters to represent all 256 octets,
|
||||||
|
instead of using "." for unprintable characters.
|
||||||
|
|
||||||
|
|
||||||
|
p4split -- split a pcap file by IPv4 address CIDR
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Splits a pcap file up ito 256 files, based on a CIDR. For example:
|
||||||
|
|
||||||
|
p4split 32 < foo.pcap
|
||||||
|
|
||||||
|
Will create [0-255].pcap
|
||||||
|
|
||||||
|
|
||||||
|
pcat -- print text representation of pcap file
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Prints a (lossy) text representation of a pcap file to stdout.
|
||||||
|
This program is the keystone of the Fluffy Suite.
|
||||||
|
By representing everything as text,
|
||||||
|
programmers can use any number of standard Unix text processing tools,
|
||||||
|
such as sed, awk, cut, grep, or head.
|
||||||
|
|
||||||
|
|
||||||
|
pmerge -- merge pcap files
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
Takes a list of pcap files, assuming they are sorted by time
|
||||||
|
(you would have to work hard to create any other kind),
|
||||||
|
and merges them into a single sorted output.
|
||||||
|
|
||||||
|
|
||||||
|
printfesc -- printf escape input
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Reads octets,
|
||||||
|
writes a string suitable for copy-paste into printf.
|
||||||
|
|
||||||
|
|
||||||
|
puniq -- omit repeated frames
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Removes duplicate frames from input,
|
||||||
|
writing to output.
|
||||||
|
|
||||||
|
|
||||||
|
pyesc -- python escape input
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Escapes input octets for pasting into a python "print" statement.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
unhex -- unescape hex
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
Reads ASCII hex codes on stdin,
|
||||||
|
writes those octets to stdout.
|
||||||
|
|
||||||
|
The following pipe is equivalent to "cat":
|
||||||
|
|
||||||
|
./hd | cut -b 11-58 | ./unhex
|
||||||
|
|
||||||
|
|
||||||
|
xor -- xor mask octets
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
Applies the given mask as an xor to input.
|
||||||
|
The mask will be repeated,
|
||||||
|
so for a 1-value mask, every octet is xored against that value.
|
||||||
|
For a 16-value mask, the mask is applied to 16-octet chunks at a time.
|
||||||
|
|
||||||
|
The "-x" option treats values as hex.
|
||||||
|
|
||||||
|
The following pipe is equivalent to "cat":
|
||||||
|
|
||||||
|
./xor 42 | ./xor -x 2A
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const charset = "" +
|
||||||
|
"·☺☻♥♦♣♠•◘○◙♂♀♪♫☼" +
|
||||||
|
"►◄↕‼¶§▬↨↑↓→←∟↔▲▼" +
|
||||||
|
" !\"#$%&'()*+,-./" +
|
||||||
|
"0123456789:;<=>?" +
|
||||||
|
"@ABCDEFGHIJKLMNO" +
|
||||||
|
"PQRSTUVWXYZ[\\]^_" +
|
||||||
|
"`abcdefghijklmno" +
|
||||||
|
"pqrstuvwxyz{|}~⌂" +
|
||||||
|
|
||||||
|
"ÇüéâäàåçêëèïîìÄÅ" +
|
||||||
|
"ÉæÆôöòûùÿÖÜ¢£¥€ƒ" +
|
||||||
|
"áíóúñѪº½⅓¼⅕⅙⅛«»" +
|
||||||
|
"░▒▓│┤╡╢╖╕╣║╗╝╜╛┐" +
|
||||||
|
"└┴┬├─┼╞╟╚╔╩╦╠═╬╧" +
|
||||||
|
"╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀" +
|
||||||
|
"αßΓπΣσµτΦΘΩδ∞φε∩" +
|
||||||
|
"⁰¹²³⁴⁵⁶⁷⁸⁹ⁱⁿ⁽⁼⁾¤"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
offset := 0
|
||||||
|
b := make([]byte, 16)
|
||||||
|
lb := make([]byte, 16)
|
||||||
|
chars := []rune(charset)
|
||||||
|
skipping := false
|
||||||
|
|
||||||
|
for ;; {
|
||||||
|
n, err := os.Stdin.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
fmt.Println("read failed:", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset > 0) && (bytes.Equal(lb, b)) {
|
||||||
|
if ! skipping {
|
||||||
|
fmt.Println("*")
|
||||||
|
}
|
||||||
|
skipping = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
copy(lb, b)
|
||||||
|
skipping = false
|
||||||
|
|
||||||
|
fmt.Printf("%08x ", offset)
|
||||||
|
offset += n
|
||||||
|
|
||||||
|
for i := 0; i < 16; i += 1 {
|
||||||
|
if i % 8 == 0 {
|
||||||
|
fmt.Printf(" ")
|
||||||
|
}
|
||||||
|
if i < n {
|
||||||
|
fmt.Printf("%02x ", b[i])
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("ǀ")
|
||||||
|
for i := 0; i < n; i += 1 {
|
||||||
|
fmt.Printf("%c", chars[b[i]])
|
||||||
|
}
|
||||||
|
fmt.Printf("ǀ\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%08x\n", offset)
|
||||||
|
}
|
62
pcat.c
62
pcat.c
|
@ -1,3 +1,17 @@
|
||||||
|
/*
|
||||||
|
* pcat.c -- pcap to text
|
||||||
|
*
|
||||||
|
* Reads pcap on stdin, dumps text representation to stdout.
|
||||||
|
* This discards or ignores some parts of packets. If fidelity is a concern,
|
||||||
|
* deal directly with the original pcap.
|
||||||
|
*
|
||||||
|
* Output lines are of the form:
|
||||||
|
*
|
||||||
|
* PROTO SRC DST OPTS PAYLOAD
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "pcap.h"
|
#include "pcap.h"
|
||||||
|
@ -31,33 +45,33 @@ print_payload(struct stream *s)
|
||||||
void
|
void
|
||||||
process_tcp(struct stream *s, char *saddr_s, char *daddr_s)
|
process_tcp(struct stream *s, char *saddr_s, char *daddr_s)
|
||||||
{
|
{
|
||||||
uint16_t sport = read_uint16be(s);
|
uint16_t sport = read_uint16(s);
|
||||||
uint16_t dport = read_uint16be(s);
|
uint16_t dport = read_uint16(s);
|
||||||
uint32_t seq = read_uint32be(s);
|
uint32_t seq = read_uint32(s);
|
||||||
uint32_t ack = read_uint32be(s);
|
uint32_t ack = read_uint32(s);
|
||||||
uint8_t off = read_uint8(s);
|
uint8_t off = read_uint8(s);
|
||||||
uint8_t hlen = (off >> 4) * 4;
|
uint8_t hlen = (off >> 4) * 4;
|
||||||
uint8_t flags = read_uint8(s);
|
uint8_t flags = read_uint8(s);
|
||||||
uint16_t window = read_uint16be(s);
|
uint16_t window = read_uint16(s);
|
||||||
uint16_t chksum = read_uint16be(s);
|
uint16_t chksum = read_uint16(s);
|
||||||
uint16_t urgent = read_uint16be(s);
|
uint16_t urgent = read_uint16(s);
|
||||||
|
|
||||||
if (hlen < 20) {
|
if (hlen < 20) {
|
||||||
printf("!");
|
printf("!");
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("TCP %s,%u,%u %s,%u,%u ", saddr_s, sport, seq, daddr_s, dport, ack);
|
printf("TCP %s,%u %s,%u %u,%u,%d ", saddr_s, sport, daddr_s, dport, seq, ack, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
process_udp(struct stream *s, char *saddr_s, char *daddr_s)
|
process_udp(struct stream *s, char *saddr_s, char *daddr_s)
|
||||||
{
|
{
|
||||||
uint16_t sport = read_uint16be(s);
|
uint16_t sport = read_uint16(s);
|
||||||
uint16_t dport = read_uint16be(s);
|
uint16_t dport = read_uint16(s);
|
||||||
uint16_t len = read_uint16be(s);
|
uint16_t len = read_uint16(s);
|
||||||
uint16_t chksum = read_uint16be(s);
|
uint16_t chksum = read_uint16(s);
|
||||||
|
|
||||||
printf("UDP %s,%u %s,%u ", saddr_s, sport, daddr_s, dport);
|
printf("UDP %s,%u %s,%u 0 ", saddr_s, sport, daddr_s, dport);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -65,7 +79,7 @@ process_icmp(struct stream *s, char *saddr_s, char *daddr_s)
|
||||||
{
|
{
|
||||||
uint8_t type = read_uint8(s);
|
uint8_t type = read_uint8(s);
|
||||||
uint8_t code = read_uint8(s);
|
uint8_t code = read_uint8(s);
|
||||||
uint16_t checksum = read_uint16be(s);
|
uint16_t checksum = read_uint16(s);
|
||||||
|
|
||||||
printf("ICMP %s %s %d ", saddr_s, daddr_s, code);
|
printf("ICMP %s %s %d ", saddr_s, daddr_s, code);
|
||||||
}
|
}
|
||||||
|
@ -76,14 +90,14 @@ process_ip4(struct stream *s)
|
||||||
uint8_t vhl = read_uint8(s);
|
uint8_t vhl = read_uint8(s);
|
||||||
uint8_t ihl = (vhl & 0x0f) * 4;
|
uint8_t ihl = (vhl & 0x0f) * 4;
|
||||||
uint8_t tos = read_uint8(s);
|
uint8_t tos = read_uint8(s);
|
||||||
uint16_t length = read_uint16be(s);
|
uint16_t length = read_uint16(s);
|
||||||
uint16_t id = read_uint16be(s);
|
uint16_t id = read_uint16(s);
|
||||||
uint16_t off = read_uint16be(s);
|
uint16_t off = read_uint16(s);
|
||||||
uint8_t ttl = read_uint8(s);
|
uint8_t ttl = read_uint8(s);
|
||||||
uint8_t proto = read_uint8(s);
|
uint8_t proto = read_uint8(s);
|
||||||
uint16_t chksum = read_uint16be(s);
|
uint16_t chksum = read_uint16(s);
|
||||||
uint32_t saddr = read_uint32be(s);
|
uint32_t saddr = read_uint32(s);
|
||||||
uint32_t daddr = read_uint32be(s);
|
uint32_t daddr = read_uint32(s);
|
||||||
|
|
||||||
char saddr_s[20];
|
char saddr_s[20];
|
||||||
char daddr_s[20];
|
char daddr_s[20];
|
||||||
|
@ -125,12 +139,12 @@ print_ethernet(struct stream *s)
|
||||||
|
|
||||||
sread(s, &saddr, sizeof(saddr));
|
sread(s, &saddr, sizeof(saddr));
|
||||||
sread(s, &daddr, sizeof(daddr));
|
sread(s, &daddr, sizeof(daddr));
|
||||||
ethertype = read_uint16be(s);
|
ethertype = read_uint16(s);
|
||||||
|
|
||||||
if (ethertype == 0x8100) {
|
if (ethertype == 0x8100) {
|
||||||
// VLAN
|
// VLAN
|
||||||
read_uint16be(s);
|
read_uint16(s);
|
||||||
ethertype = read_uint16be(s);
|
ethertype = read_uint16(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ethertype) {
|
switch (ethertype) {
|
||||||
|
@ -146,7 +160,7 @@ print_frame(struct pcap_file *p, struct pcap_pkthdr *hdr, char const *frame)
|
||||||
struct stream streambuf;
|
struct stream streambuf;
|
||||||
struct stream *s = &streambuf;
|
struct stream *s = &streambuf;
|
||||||
|
|
||||||
sinit(s, frame, hdr->caplen);
|
sinit(s, frame, hdr->caplen, ENDIAN_BIG);
|
||||||
printf("%u.%u ", hdr->ts.tv_sec, hdr->ts.tv_usec);
|
printf("%u.%u ", hdr->ts.tv_sec, hdr->ts.tv_usec);
|
||||||
switch (p->linktype) {
|
switch (p->linktype) {
|
||||||
case LINKTYPE_ETHERNET:
|
case LINKTYPE_ETHERNET:
|
||||||
|
|
22
printfesc.c
22
printfesc.c
|
@ -1,4 +1,5 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
|
@ -11,13 +12,24 @@ main(int argc, char *argv[])
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case EOF:
|
case EOF:
|
||||||
break;
|
break;
|
||||||
case '0' ... '9':
|
case '\n':
|
||||||
case 'A' ... 'Z':
|
printf("\\n");
|
||||||
case 'a' ... 'z':
|
break;
|
||||||
putchar(c);
|
case '\r':
|
||||||
|
printf("\\r");
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
printf("\\t");
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
printf("\\\"");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("\\\\%03o", c);
|
if (isprint(c)) {
|
||||||
|
putchar(c);
|
||||||
|
} else {
|
||||||
|
printf("\\%03o", c);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
43
stream.c
43
stream.c
|
@ -5,10 +5,11 @@
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
sinit(struct stream *s, char const *buf, size_t buflen)
|
sinit(struct stream *s, char const *buf, size_t buflen, enum endianness endian)
|
||||||
{
|
{
|
||||||
s->buf = buf;
|
s->buf = buf;
|
||||||
s->len = buflen;
|
s->len = buflen;
|
||||||
|
s->endian = endian;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -25,7 +26,7 @@ sskip(struct stream *s, size_t count)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sread(struct stream * s, void *buf, size_t count)
|
sread(struct stream *s, void *buf, size_t count)
|
||||||
{
|
{
|
||||||
void const *d = s->buf;
|
void const *d = s->buf;
|
||||||
|
|
||||||
|
@ -37,8 +38,10 @@ sread(struct stream * s, void *buf, size_t count)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint8_t
|
uint8_t
|
||||||
read_uint8(struct stream * s)
|
read_uint8(struct stream *s)
|
||||||
{
|
{
|
||||||
uint8_t *d = (uint8_t *) s->buf;
|
uint8_t *d = (uint8_t *) s->buf;
|
||||||
|
|
||||||
|
@ -48,8 +51,10 @@ read_uint8(struct stream * s)
|
||||||
return d[0];
|
return d[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
uint16_t
|
uint16_t
|
||||||
read_uint16be(struct stream * s)
|
read_uint16be(struct stream *s)
|
||||||
{
|
{
|
||||||
uint8_t *d = (uint8_t *) s->buf;
|
uint8_t *d = (uint8_t *) s->buf;
|
||||||
|
|
||||||
|
@ -59,8 +64,9 @@ read_uint16be(struct stream * s)
|
||||||
return ((d[0] << 8) | (d[1] << 0));
|
return ((d[0] << 8) | (d[1] << 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint16_t
|
uint16_t
|
||||||
read_uint16le(struct stream * s)
|
read_uint16le(struct stream *s)
|
||||||
{
|
{
|
||||||
uint8_t *d = (uint8_t *) s->buf;
|
uint8_t *d = (uint8_t *) s->buf;
|
||||||
|
|
||||||
|
@ -70,11 +76,21 @@ read_uint16le(struct stream * s)
|
||||||
return ((d[0] << 0) | (d[1] << 8));
|
return ((d[0] << 0) | (d[1] << 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t
|
||||||
|
read_uint16(struct stream *s)
|
||||||
|
{
|
||||||
|
if (s->endian == ENDIAN_BIG) {
|
||||||
|
return read_uint16be(s);
|
||||||
|
} else {
|
||||||
|
return read_uint16le(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
read_uint32be(struct stream * s)
|
read_uint32be(struct stream *s)
|
||||||
{
|
{
|
||||||
uint8_t *d = (uint8_t *) s->buf;
|
uint8_t *d = (uint8_t *) s->buf;
|
||||||
|
|
||||||
if (!sskip(s, 4)) {
|
if (!sskip(s, 4)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -82,12 +98,21 @@ read_uint32be(struct stream * s)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t
|
uint32_t
|
||||||
read_uint32le(struct stream * s)
|
read_uint32le(struct stream *s)
|
||||||
{
|
{
|
||||||
uint8_t *d = (uint8_t *) s->buf;
|
uint8_t *d = (uint8_t *) s->buf;
|
||||||
|
|
||||||
if (!sskip(s, 4)) {
|
if (!sskip(s, 4)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return ((d[0] << 0) | (d[1] << 8) | (d[2] << 16) | (d[3] << 24));
|
return ((d[0] << 0) | (d[1] << 8) | (d[2] << 16) | (d[3] << 24));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
read_uint32(struct stream *s)
|
||||||
|
{
|
||||||
|
if (s->endian == ENDIAN_BIG) {
|
||||||
|
return read_uint32be(s);
|
||||||
|
} else {
|
||||||
|
return read_uint32le(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
10
stream.h
10
stream.h
|
@ -5,18 +5,26 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
enum endianness {
|
||||||
|
ENDIAN_BIG,
|
||||||
|
ENDIAN_LITTLE
|
||||||
|
};
|
||||||
|
|
||||||
struct stream {
|
struct stream {
|
||||||
char const *buf;
|
char const *buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
enum endianness endian;
|
||||||
};
|
};
|
||||||
|
|
||||||
void sinit(struct stream *s, char const *buf, size_t len);
|
void sinit(struct stream *s, char const *buf, size_t len, enum endianness endian);
|
||||||
bool sskip(struct stream *s, size_t count);
|
bool sskip(struct stream *s, size_t count);
|
||||||
bool sread(struct stream *s, void *buf, size_t count);
|
bool sread(struct stream *s, void *buf, size_t count);
|
||||||
uint8_t read_uint8(struct stream *s);
|
uint8_t read_uint8(struct stream *s);
|
||||||
uint16_t read_uint16be(struct stream *s);
|
uint16_t read_uint16be(struct stream *s);
|
||||||
uint16_t read_uint16le(struct stream *s);
|
uint16_t read_uint16le(struct stream *s);
|
||||||
|
uint16_t read_uint16(struct stream *s);
|
||||||
uint32_t read_uint32be(struct stream *s);
|
uint32_t read_uint32be(struct stream *s);
|
||||||
uint32_t read_uint32le(struct stream *s);
|
uint32_t read_uint32le(struct stream *s);
|
||||||
|
uint32_t read_uint32(struct stream *s);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue