1# This file is part of Scapy
2# See http://www.secdev.org/projects/scapy for more information
3# Copyright (C) Lucas Preston <lucas.preston@infinite.io>
4# This program is published under a GPLv2 license
5
6# scapy.contrib.description = NFS Mount v3
7# scapy.contrib.status = loads
8
9from scapy.contrib.oncrpc import RPC, RPC_Call
10from scapy.packet import Packet, bind_layers
11from scapy.fields import IntField, StrLenField, IntEnumField, PacketField, \
12    ConditionalField, FieldListField
13from scapy.contrib.nfs import File_Object
14
15mountstat3 = {
16    0: 'MNT3_OK',
17    1: 'MNT3ERR_PERM',
18    2: 'MNT3ERR_NOENT',
19    5: 'MNT3ERR_IO',
20    13: 'MNT3ERR_ACCES',
21    20: 'MNT3ERR_NOTDIR',
22    22: 'MNT3ERR_INVAL',
23    63: 'MNT3ERR_NAMETOOLONG',
24    10004: 'MNT3ERR_NOTSUPP',
25    10006: 'MNT3ERR_SERVERFAULT'
26}
27
28
29class Path(Packet):
30    name = 'Path'
31    fields_desc = [
32        IntField('length', 0),
33        StrLenField('path', '', length_from=lambda pkt: pkt.length),
34        StrLenField('fill', '', length_from=lambda pkt: (4 - pkt.length) % 4)
35    ]
36
37    def extract_padding(self, s):
38        return '', s
39
40    def set(self, path, length=None, fill=None):
41        if length is None:
42            length = len(path)
43        if fill is None:
44            fill = b'\x00' * ((4 - len(path)) % 4)
45        self.length = length
46        self.path = path
47        self.fill = fill
48
49
50class NULL_Call(Packet):
51    name = 'MOUNT NULL Call'
52    fields_desc = []
53
54
55class NULL_Reply(Packet):
56    name = 'MOUNT NULL Reply'
57    fields_desc = []
58
59
60bind_layers(RPC, NULL_Call, mtype=0)
61bind_layers(RPC, NULL_Reply, mtype=1)
62bind_layers(RPC_Call, NULL_Call, program=100005, procedure=0, pversion=3)
63
64
65class MOUNT_Call(Packet):
66    name = 'MOUNT Call'
67    fields_desc = [
68        PacketField('path', Path(), Path)
69    ]
70
71
72class MOUNT_Reply(Packet):
73    name = 'MOUNT Reply'
74    fields_desc = [
75        IntEnumField('status', 0, mountstat3),
76        ConditionalField(
77            PacketField('filehandle', File_Object(), File_Object),
78            lambda pkt: pkt.status == 0
79        ),
80        ConditionalField(IntField('flavors', 0), lambda pkt: pkt.status == 0),
81        ConditionalField(
82            FieldListField(
83                'flavor', None, IntField('', None),
84                count_from=lambda pkt: pkt.flavors
85            ),
86            lambda pkt: pkt.status == 0
87        )
88    ]
89
90    def get_filehandle(self):
91        if self.status == 0:
92            return self.filehandle.fh
93        return None
94
95
96bind_layers(RPC, MOUNT_Call, mtype=0)
97bind_layers(RPC, MOUNT_Reply, mtype=1)
98bind_layers(RPC_Call, MOUNT_Call, program=100005, procedure=1, pversion=3)
99
100
101class UNMOUNT_Call(Packet):
102    name = 'UNMOUNT Call'
103    fields_desc = [
104        PacketField('path', Path(), Path)
105    ]
106
107
108class UNMOUNT_Reply(Packet):
109    name = 'UNMOUNT Reply'
110    fields_desc = []
111
112
113bind_layers(RPC, UNMOUNT_Call, mtype=0)
114bind_layers(RPC, UNMOUNT_Reply, mtype=1)
115bind_layers(
116    RPC_Call, UNMOUNT_Call, program=100005, procedure=3, pversion=3
117)
118