1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.connectivity.mdns;
18 
19 import static com.android.server.connectivity.mdns.util.MdnsUtils.ensureRunningOnHandlerThread;
20 
21 import android.annotation.NonNull;
22 import android.os.Handler;
23 import android.os.ParcelFileDescriptor;
24 import android.system.Os;
25 
26 import com.android.net.module.util.FdEventsReader;
27 import com.android.server.connectivity.mdns.util.MdnsUtils;
28 
29 import java.io.FileDescriptor;
30 import java.net.InetSocketAddress;
31 import java.util.Set;
32 
33 /** Simple reader for mDNS packets. */
34 public class MulticastPacketReader extends FdEventsReader<MulticastPacketReader.RecvBuffer> {
35     @NonNull
36     private final String mLogTag;
37     @NonNull
38     private final ParcelFileDescriptor mSocket;
39     @NonNull
40     private final Handler mHandler;
41     @NonNull
42     private final Set<PacketHandler> mPacketHandlers = MdnsUtils.newSet();
43 
44     interface PacketHandler {
45         /**
46          * Handle an incoming packet.
47          *
48          * The recvbuf and src <b>will be reused and modified</b> after this method returns, so
49          * implementers must ensure that they are not accessed after handlePacket returns.
50          */
handlePacket(byte[] recvbuf, int length, InetSocketAddress src)51         void handlePacket(byte[] recvbuf, int length, InetSocketAddress src);
52     }
53 
54     public static final class RecvBuffer {
55         final byte[] data;
56         final InetSocketAddress src;
57 
RecvBuffer(byte[] data, InetSocketAddress src)58         private RecvBuffer(byte[] data, InetSocketAddress src) {
59             this.data = data;
60             this.src = src;
61         }
62     }
63 
64     /**
65      * Create a new {@link MulticastPacketReader}.
66      * @param socket Socket to read from. This will *not* be closed when the reader terminates.
67      * @param buffer Buffer to read packets into. Will only be used from the handler thread.
68      * @param port the port number for the socket
69      */
MulticastPacketReader(@onNull String interfaceTag, @NonNull ParcelFileDescriptor socket, @NonNull Handler handler, @NonNull byte[] buffer)70     protected MulticastPacketReader(@NonNull String interfaceTag,
71             @NonNull ParcelFileDescriptor socket, @NonNull Handler handler,
72             @NonNull byte[] buffer) {
73         // Set the port to zero as placeholder as the recvfrom() call will fill the actual port
74         // value later.
75         super(handler, new RecvBuffer(buffer, new InetSocketAddress(0 /* port */)));
76         mLogTag = MulticastPacketReader.class.getSimpleName() + "/" + interfaceTag;
77         mSocket = socket;
78         mHandler = handler;
79     }
80 
81     @Override
recvBufSize(@onNull RecvBuffer buffer)82     protected int recvBufSize(@NonNull RecvBuffer buffer) {
83         return buffer.data.length;
84     }
85 
86     @Override
createFd()87     protected FileDescriptor createFd() {
88         // Keep a reference to the PFD as it would close the fd in its finalizer otherwise
89         return mSocket.getFileDescriptor();
90     }
91 
92     @Override
onStop()93     protected void onStop() {
94         // Do nothing (do not close the FD)
95     }
96 
97     @Override
readPacket(@onNull FileDescriptor fd, @NonNull RecvBuffer buffer)98     protected int readPacket(@NonNull FileDescriptor fd, @NonNull RecvBuffer buffer)
99             throws Exception {
100         return Os.recvfrom(
101                 fd, buffer.data, 0, buffer.data.length, 0 /* flags */, buffer.src);
102     }
103 
104     @Override
handlePacket(@onNull RecvBuffer recvbuf, int length)105     protected void handlePacket(@NonNull RecvBuffer recvbuf, int length) {
106         for (PacketHandler handler : mPacketHandlers) {
107             handler.handlePacket(recvbuf.data, length, recvbuf.src);
108         }
109     }
110 
111     /**
112      * Add a packet handler to deal with received packets. If the handler is already set,
113      * this is a no-op.
114      */
addPacketHandler(@onNull PacketHandler handler)115     public void addPacketHandler(@NonNull PacketHandler handler) {
116         ensureRunningOnHandlerThread(mHandler);
117         mPacketHandlers.add(handler);
118     }
119 
120     /**
121      * Remove a packet handler added via {@link #addPacketHandler}. If the handler was not set,
122      * this is a no-op.
123      */
removePacketHandler(@onNull PacketHandler handler)124     public void removePacketHandler(@NonNull PacketHandler handler) {
125         ensureRunningOnHandlerThread(mHandler);
126         mPacketHandlers.remove(handler);
127     }
128 }
129 
130